題目是這樣的: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 <stdlib.h>
int main(){
FILE * out;
int total = 3000;
int a[] = { 1, 1, 1}; // 記錄每段 長度, 總長度為 1000 km
int t[] = { 0, 0, 0}; // 記錄每段路 返回經過的次數
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>= 1000) continue; // x + y < 3000, 所以2 * x < 2000, 即 x < 1000
if(xy>= 3000) continue; // 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>= 1000) continue; // x + y < 3000, 所以2 * x < 2000, 即 x < 1000
if(xy>= 3000) continue; // 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, 使用循環不靠譜了,不過應該可以用回溯法來實現。
這種方法的另一個漏洞是,只能遍歷整數,而數學上的最優解,是一個小數。這一點我還無法克服。