Bone Collector II HDU - 2639 01背包第k最大值
题意:
01背包,找出第k最优解
题解:
对于01背包最优解我们肯定都很熟悉
第k最优解的话也就是在dp方程上加一个维度来存它的第k最优解(dp[i][j]代表,体积为i能获得的第j最大价值)
对于每一个物品只有两种选择情况
1、把这个物品加入背包
2、不要这个物品
那么它的前k种最优解也是由n种物品的这两个选择组成的
假设总体积是4,现在有两种物品,求第2最大值
1、体积1,价值3
2、体积1,价值2
最开始dp状态:
体积: 1 2 3 4
第1最大值:0 0 0 0
第2最大值:0 0 0 0
x、y数组是用来记录他从上一个状态转移过来的前k最大值。
x数组代表需要这个物品的时候的前k最大值
y数组代表不需要这个物品的时候前k最大值
下面演示过程:
体积为4的时候
x[1]=v[4-1][1]+3 =3
x[2]=v[4-1][2]+3 =3
y[1]=v[4][1]=0
y[2]=v[4][2]=0
下面那个while循环的意思就是给x数组和y数组这4个数(因为我要求第2(k)最大值,所以是2*2(2*k)个数)从大到小排序之后去重,从中挑出来2(k)个赋值给v[j][1],v[j][2](v[j][1]...v[j][k])
排序后:3 3 0 0,去重后3 0
那么v[4][1]=3,v[4][2]=0
可能有些人不明白while代码中怎么会有去重操作,我们来看一下
给v[4][1]赋值的时候因为x[1]=3>y[1]=0,所以很自然v[4][1]=3
这个时候给v[4][2]赋值,因为x[2]=3>y[1]=0,所以v[4][2]=3。但是因为v[4][1]==v[4][2]所以循环不会结束
我们来看一下循环结束条件while((m<=f || n<=f) && o<=f)
之有前k个最优值都找到之后循环会结束,或者x和y数组所有值都用完了
所以还是去给v[4][2]赋值,那么又因为x[3]=y[3]=-1。所以x[3]=-1<y[0]=0,那么v[4][2]=0
这个时候循环就结束了
我上面解释的时候用的是前k最大值,代码中用的字母是f。。。
代码:
1 #include<stdio.h> 2 #include<string.h> 3 int q[105],w[105],v[1005][35],x[1005],y[1005]; 4 int main() 5 { 6 int a,s,sum,d,f,g; 7 scanf("%d",&a); 8 while(a--) 9 { 10 memset(v,0,sizeof(v)); 11 scanf("%d%d%d",&s,&d,&f); 12 for(int i=1; i<=s; ++i) 13 { 14 scanf("%d",&w[i]); 15 sum+=w[i]; 16 } 17 for(int i=1; i<=s; ++i) 18 scanf("%d",&q[i]); 19 for(int i=1; i<=s; ++i) 20 { 21 for(int j=d; j>=q[i]; --j) 22 { 23 int k; 24 for(k=1; k<=f; ++k) 25 { 26 x[k]=v[j-q[i]][k]+w[i]; 27 y[k]=v[j][k]; 28 } 29 x[k]=y[k]=-1; 30 int m,n,o; 31 m=n=o=1; 32 while((m<=f || n<=f) && o<=f) 33 { 34 if(x[m]>y[n]) 35 { 36 v[j][o]=x[m]; 37 ++m; 38 } 39 else 40 { 41 v[j][o]=y[n]; 42 ++n; 43 } 44 if(v[j][o]!=v[j][o-1]) ++o; 45 } 46 } 47 } 48 printf("%d\n",v[d][f]); 49 } 50 return 0; 51 }