CheckiO上面的一道题目 Batteries Loading
这倒题目的意思就是,给出一堆电池,每个电池有不同的重量,电池装在两只手上,怎样装载,才能使两只手上的电池重量之差最小,给出了一些例程:
checkio([10,10]) == 0 checkio([10]) == 10 checkio([5, 8, 13, 27, 14]) == 3 checkio([5,5,6,5]) == 1 checkio([12, 30, 30, 32, 42, 49]) == 9 checkio([1, 1, 1, 3]) == 0
这道题很明显是动态规划问题,但我对动态规划并不是很熟悉,于是看了一阵时间的背包问题。
事实上,这道题可以转化为背包问题,即,要使两只手臂装载的电池重量差最小,那应该让每只手臂越接近总重量的一半,才能使其差最小。
在此,背包的容量就是总重量sum的一半,即halfsum,将所给的电池装入一个背包容量为halfsum的背包中,怎样装,能使箱子的重量达到最大?
所以,首要问题,即找到最优子结构
问题是当前阶段的剩余空间最小,并不意味下一阶段的剩余空间也一定最小,即该问题并不具备最优子结构的特征。但如果将装背包的体积作为状态的话,则阶段间的状态转移关系顺其自然,可使得最优化问题变为判定性问题。设状态转移方程
f[i,j]——在前i个物品中选择若干个物品(必须包括物品i)装箱,其体积正好为j的标志。显然f[i,j]=f[i-1,j-box[i]],即物品i装入箱子后的体积正好为j的前提是f[i-1,j-box[i]]=true。初始时,f[0,0]=true(1≤i≤n,box[i]≤j≤halfsum)。
我首先用C++代码实现,因为现在对C++较为熟悉:
1 #include <iostream> 2 #include <stdlib.h> 3 #include <cstring> 4 5 #define maxlength 100 6 7 using namespace std; 8 9 int things[maxlength]; 10 11 int main() 12 { 13 cout << "Hello World!" << endl; 14 int in = 0; 15 int n; 16 17 while(cin >> n && cin.good()) 18 { 19 things[in] = n; 20 in++; 21 } 22 23 int sum = 0; 24 25 if(in == 1) 26 { 27 cout << "Min minus is " << things[0] << endl; 28 29 return 0; 30 } 31 32 for(int i = 0; i < in; i++) 33 { 34 sum += things[i]; 35 } 36 37 /* 取总数值的一半作为背包的容量 38 */ 39 int halfsum = sum/2; 40 41 int* f0 = (int* )malloc((halfsum + 1) * sizeof(int)); 42 int* f1 = (int* )malloc((halfsum + 1) * sizeof(int)); 43 44 memset(f0, 0, (halfsum + 1)*sizeof(int)); 45 memset(f1, 0, (halfsum + 1)*sizeof(int)); 46 47 f0[0] = 1; 48 49 /* 50 *装入背包, f0记录i-1时刻背包的状态 51 *f1记录i时刻背包的状态 52 *f0[j]表示当前容量为j的背包恰好装满时,其值为1,否则为0, 同理f1[j] 53 *初始状态,只有f0[0]为1 54 *对于第i个物品,当f0[j-things[i]] = 1时,说明装入i物品,能使背包恰好装满,即f1[j] = 1, 即当前状态更新 55 */ 56 for(int i = 0;i < in;i++) 57 { 58 for(int j = things[i];j <= halfsum; j++) 59 { 60 if(f0[j-things[i]]) 61 { 62 f1[j] = 1; 63 } 64 } 65 for(int k = 1;k <= halfsum; k++) 66 { 67 f0[k]=f1[k]; 68 } 69 } 70 71 int k = halfsum; 72 while(!f0[k]) 73 { 74 k--; 75 } 76 77 /* 78 *因为有两只背包,当其中一直装了k个时, 另一个有sum-k个 79 *则差为 sum-k-k 80 */ 81 int minminus = sum - 2*k; 82 83 cout << "Min minus is " << minminus << endl; 84 85 return 0; 86 }
其次转化为python的代码,这里碰到一个小问题,对于python而言, 如果令halfsum = sum/2的话,python会自动将halfsum默认设置为float型,所以需要前面加个int转换一下:
1 def checkio(stones): 2 ''' 3 minimal possible weight difference between stone piles 4 ''' 5 slen = len(stones) 6 7 if slen == 1 : 8 return stones[0] 9 10 sum = 0 11 for i in stones : 12 sum = sum + i 13 14 halfsum = int(sum/2) 15 16 f0 = [0] * (halfsum + 1) 17 f1 = [0] * (halfsum + 1) 18 19 f0[0] = 1 20 21 for i in stones : 22 for j in range(i, halfsum+1) : 23 if f0[j-i] : 24 f1[j] = 1 25 for k in range(1, halfsum+1) : 26 f0[k] = f1[k] 27 28 k = halfsum 29 while f0[k] == 0: 30 k = k-1 31 32 minminus = sum - 2*k 33 34 return minminus 35 36 37 if __name__ == '__main__': 38 assert checkio([10,10]) == 0, 'First, with equal weights' 39 assert checkio([10]) == 10, 'Second, with a single stone' 40 assert checkio([5, 8, 13, 27, 14]) == 3, 'Third' 41 assert checkio([5,5,6,5]) == 1, 'Fourth' 42 assert checkio([12, 30, 30, 32, 42, 49]) == 9, 'Fifth' 43 assert checkio([1, 1, 1, 3]) == 0, "Six, don't forget - you can hold different quantity of parts" 44 print ('All is ok')
当然,此题也有暴力解法,即用python的函数,求得输入序列的全排列,然后枚举每一个组合的差值,最后从其中选出最小值。