题目链接:https://vjudge.net/problem/POJ-1015
好题。首先这个状态设计的就比较巧妙:设f[i][j][k]表示前i人选j个,控方-辩方的差值为k,能得到的最大和。这儿把控方和辩方的差值作为状态的一维(可以理解成背包问题的“体积”)。
那么就有转移方程:f[i][j][k] = max(f[i-1][j][k],f[i-1][j-1][k-(p[i]-d[i])]+p[i]+d[i])。然后注意到这个差值可能为负数,所以要把数组下标都加上400,这个在白书习题poj2184里也用到了。最后是打印方案,因为用的是三维的f[i][j][k],所以这儿方案打印就比较方便,详细见代码。应该可以省去第一维的i,但那样记录方案就有点麻(不)烦(会)
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int f[210][30][810],p[210],d[210],ans[30]; int n,m,i,j,k,ansp,ansd,res,dif,cnt,now; //ansp,ansd分别记录最后p方和d方的和,res记录最大和,dif是最小差值 int main(){ while ((scanf("%d%d",&n,&m))&&n){ for (i=1;i<=n;i++) scanf("%d%d",&p[i],&d[i]); memset(f,-0x3f,sizeof(f)); for (i=0;i<=n;i++) f[i][0][400]=0; //*初始化,注意偏移量400 for (i=1;i<=n;i++) for (j=1;j<=m;j++) for (k=0;k<=800;k++){ //* if (i-1>=j) f[i][j][k]=f[i-1][j][k]; if (k-p[i]+d[i]>=0&&f[i-1][j-1][k-(p[i]-d[i])]>=0) f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k-(p[i]-d[i])]+p[i]+d[i]); } i=n;j=m; res=0;dif=1e5; cnt=0; //找到最优解对应的最终状态 for (int l=0;l<=800;l++) if (f[n][m][l]>=0) if (abs(l-400)<dif||(abs(l-400)==dif&&res<f[n][m][l])){ k=l; dif=abs(l-400); res=f[n][m][l]; } //求出一个方案 ansp=ansd=0; while (j>0){ if (f[i][j][k]==f[i-1][j][k]) i--; else{ ansp+=p[i]; ansd+=d[i]; ans[++cnt]=i; k-=(p[i]-d[i]); i--;j--; //* } } printf("Jury #%d ",++now); printf("Best jury has value %d for prosecution and value %d for defence: ",ansp,ansd); for (i=cnt;i>=2;i--) printf("%d ",ans[i]); printf("%d ",ans[1]); } return 0; }