火車運煤問題(驢運蘿卜問題)


題目是這樣的:A B兩地距離1000km,A地有3000噸煤。A地有一輛火車,單次最大運載量為1000噸,每行駛 1km要消耗 1噸煤。現在要把這3000噸煤從A運輸到B,求運到B最多還有多少噸煤?

 對於這個問題,我想到了創建一個模型,根據題目要求得出初始狀態、終極狀態和中間條件,這里用一張圖表示。

 初始狀態:A=3000, x = y = 0, B = 0

 結束狀態:A= 0, y - x = 1000, B + x + y = 3000

這里  火車單次最大負載 maxLoad = 1000,  煤總量 為 3000, 因此將路程 3000/1000 = 3 比較合適(這里不做證明)

 

看圖分析可得,對於每段路程,往次數總是比返次數多1 ,所以可以推出  a[0] + a[1] + a[2] = 1000

所有條件均已在途中列出。 如果不能理解上圖,可以考慮一種實際情況 a[0] = a[1] = 250, a[2] = 500。

下一個問題,就是如何通過編程來找出B的最大值。由於  a[0] + a[1] + a[2] = 1000,因此,可以用遍歷的方法實現。

代碼如下 

#include <stdio.h>
#include <stdlib.h>
int main(){
    FILE *  out;
     int total =  3000;

     int a[] = { 111};   //  記錄每段 長度, 總長度為 1000 km
     int t[] = { 000};    //  記錄每段路  返回經過的次數
     int x =  0, xb =  0;
     int xy =  0, xyb =  0;   //  x + y
     int remain, remainb;
     int i, j;
     int max =  0;
     //  創建一個文件,用來記錄結果
     if(( out=fopen( " res.txt "" a++ "))==NULL){
        fprintf(stdout,  " Cannot open \"res.txt\" file\n ");
        exit( 1);
    }
     //  invariant: a[0] + a[1] + a[2] = 1000
    
//  invariant: 剩下的煤量超過剩下的距離
     for(i =  1; i < 500; i++){
        a[ 0] = i;     //  第一段路程  0001
        t[ 0] =  2;
        xb = a[ 0] * t[ 0];
        xyb = a[ 0] * (  2 * t[ 0] +  1);
        remainb = total - xyb;   //  記錄 x, xy, remain 以便於在內層循環起始處恢復
         for(j =  1; j <  500; j++){
            x = xb, xy = xyb, remain = remainb;   //  每次都重新對 x, xy, remain賦值
            a[ 1] = j;            //  第二段路程     0002
            t[ 1] = remain %  1000 ==  0? (remain/ 1000 -  1):(remain/ 1000);
            x += a[ 1] * t[ 1];
            xy += a[ 1] * (  2 * t[ 1] +  1);
             if(x>= 1000continue;    //  x + y < 3000, 所以2 * x < 2000, 即 x < 1000
             if(xy>= 3000continue;   //  x + y < 3000
            remain = total - xy;

            a[ 2] =  1000 - i - j;    //  第三段路程    0003
             if(a[ 2] > remain)  continue;
            t[ 2] = remain %  1000 ==  0? (remain/ 1000 -  1):(remain/ 1000);
            x += a[ 2] * t[ 2];
            xy += a[ 2] * (  2 * t[ 2] +  1);
             if(x>= 1000continue;   //  x + y < 3000, 所以2 * x < 2000, 即 x < 1000
             if(xy>= 3000continue;   //  x + y < 3000

            remain = total - xy;
             //  將 結果打印到 文件中,便於查看
            fprintf( out" i = %d, j = %d, x = %d, xy = %d, r = %d\n ", i,  j, x, xy, r);  
             if(remain > max) max = remain;
        }   //  end of innner for loop j
    }   //  end of outer for (i
    printf( " max = %d\n ", max);
     if(fclose( out)!= 0){
        fprintf(stdout,  " close failed ");
    }
    getchar();
     return  0;

} 

之所以去 a[0] , a[1] 范圍 是  1-500, 是因為除最后一段路不需要返回以外,其它都需要。需要返回,同時最大載貨量為 1000,所以前進距離最多為 500。否則火車就白跑了。

如果 煤的總量擴展到   3000 * N, 使用循環不靠譜了,不過應該可以用回溯法來實現。

這種方法的另一個漏洞是,只能遍歷整數,而數學上的最優解,是一個小數。這一點我還無法克服。

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM