分析转自:http://blog.csdn.net/dongdongzhang_/article/details/7955136
题意 : 背包能装体积为N, 有两种宝石, 数量无限, 不能切割。 分别为 size1 value 1 size2 value2
问背包能装最大的价值?
思路 : 线性规划问题。 有一组坑爹数据 100 3 3 7 7 一般的会输出 99 但是结果是 100 各10颗
线性规划知识, x, y 分别为 取两种宝石的数量 目标函数 : z = v1 * x + v 2 * y; K = -(v1 / v2);
1. x >= 0;
2. y >= 0;
3. s1 * x + s2 + y <= N; k = - (s1 / s2);
若 abs(K) > abs (k) 那么 将相交与B。 若abs(K) == abs(k) 整条直线都可以。 若 abs(K) < abs(k) 相交与A
其实 abs(K) = v1 / v2 > abs(k) = s1 / s2 正好是 价值比的比较, v1 / s1 > v2 / s2 ,v1价值大, 就应该 x 大些, 取1宝石多些。
同理 价值相同 就应该 x, y 取什么都可以, v2 价值大, 就应该 y 大些, 取2宝石多些。
但是 宝石不能切割, 所以。 就形成了一个区域, 在最优解附近。 所以区域附近的点 需要枚举。
不过有一个不变的是, 在两宝石的体积的最小公倍数内, 肯定取价值大。
而在最小公倍外的,就要分别枚举。 不能盲目的取价值大。
比如一个例子, 20 4 5 6 8 答案是 26 = 2 * 5 + 2 * 8。 若只取价值大的, 会装不满。 没取到最优解。
4 与 6 的最小公倍数是 12. 那每一个12的体积 就应该取 2个 宝石2 因为宝石2价值大。
剩余的8 就有取枚举, 0 * 5 + 1 * 6 , 或者 1 * 5 + 0 * 6, 或者 2 * 5 + 0 * 6 那么就取2个宝石1. 就能装满了, 并且价值最大。
但是 如果是 15 4 5 6 8 的话, 那么按照这方法就会输出 16 只能取到 2个 宝石2 剩余3体积, 不能取到任意宝石。
答案应该是 18 = 2 * 5 + 1 * 8, 少取一个宝石2,腾出6体积,并 利用剩余的3体积的2体积 取两颗宝石1,价值更大。
所以,面对这问题。 我们就应该至少腾出一个公倍数的空间才枚举。 不然就会出错。
1 #include<stdio.h> 2 #include<algorithm> 3 using namespace std; 4 long long gcd(long long da,long long xiao) 5 { 6 long long temp; 7 while(xiao!=0) 8 { 9 temp=da%xiao; 10 da=xiao; 11 xiao=temp; 12 } 13 return da; 14 } 15 int main() 16 { 17 int iCase=0; 18 int T; 19 scanf("%d",&T); 20 long long N,S1,V1,S2,V2; 21 while(T--) 22 { 23 iCase++; 24 scanf("%I64d%I64d%I64d%I64d%I64d",&N,&S1,&V1,&S2,&V2); 25 long long tmp=S1*S2/gcd(S1,S2); 26 long long res; 27 long long tt=N/tmp; 28 N=N%tmp; 29 if(tt) 30 { 31 tt--; 32 N+=tmp; 33 } 34 res=max((tt)*(tmp/S1)*V1,(tt)*(tmp/S2)*V2); 35 long long res2=0; 36 if(S2>S1) 37 { 38 long long t; 39 t=S1;S1=S2;S2=t; 40 t=V1;V1=V2;V2=t; 41 } 42 for(int i=0;i<=N/S1;i++) 43 { 44 if(res2<i*V1+(N-i*S1)/S2*V2)res2=i*V1+(N-i*S1)/S2*V2; 45 } 46 res+=res2; 47 printf("Case #%d: %I64d ",iCase,res); 48 } 49 return 0; 50 }