题目链接:http://poj.org/problem?id=1015
题目:
题解:
我们考虑设计DP状态(因为这很显然是一个完全背包问题不是吗?)
dp[j][k]表示在外层循环到i时,选了j个人,此时辩方总分和控方总分差值为k的时,辩方和控方的总分的和的最大值
dp[j][k+a[i]-b[i]] = max (dp[j][k + a[i] - b[i]] , dp[j][k] + a[i] + b[i])
因为是完全背包,所以我们需要写一个search函数判断当前转移的状态是否已经选过了i,这样我们需要记录一个d[j][k]数组表示在dp[j][k]的状态是选哪一个人转移过来的,这恰好也是题目要求我们处理出来的
题解其实就是上述部分了
题外话:
我们考虑不写那个search函数,然后把j这一维倒序循环,根据《算法竞赛进阶指南》,这样的做法是可以的,但是笔者尝试之后还做不到,碰到的问题就是假设dp[j][k+a[i]-b[i]]被dp[j-1][k]转移得到,此时我们可以确定dp[j-1][k]这个状态没有选择i这个人。
但是在之后的循环中,dp[j-1][k]可能会被再次更新,我们要是只是统计答案的话这并没有什么影响,但是我们在d数组回溯的时候就会得到错误的转移,因为dp[j-1][k]可能被i更新,于是d[j-1][k]变成了i。补充:但是在我的AC代码里,显然是可以避免这种情况的,因为dp[j-1][k]不可能再被更新
除此之外,我发现i循环放在最外面是不行的。这是为什么?若是有读者知道请在评论区留言。
AC代码(加了search函数)
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; int n,m,fix,time; int a[201],b[201],dp[201][801],d[201][801],id[201]; inline int read() { char ch=getchar(); int s=0,f=1; while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();} return s*f; } bool search(int j,int k,int i) { while (j&&d[j][k]!=i) { int o=d[j][k]; k-=a[o]-b[o]; j--; } if (j) return false; return true; } int main() { while (1) { n=read();m=read(); if (!n) break; for (int i=1;i<=n;i++) { a[i]=read();b[i]=read(); } memset(dp,-1,sizeof(dp)); memset(d,0,sizeof(d)); fix=m*20; dp[0][fix]=0; for (int j=1;j<=m;j++) for (int k=0;k<=fix<<1;k++) if (dp[j-1][k]>=0) { for (int i=1;i<=n;i++) if (dp[j-1][k]+a[i]+b[i]>dp[j][k+a[i]-b[i]]&&search(j-1,k,i)) { dp[j][k+a[i]-b[i]]=dp[j-1][k]+a[i]+b[i]; //if (j==3&&k+a[i]-b[i]==57&&k==58) printf("sdfs%d ",d[j-1][k]); d[j][k+a[i]-b[i]]=i; //if (j==2&&k+a[i]-b[i]==58) printf("%d ",i); } } int k,div; for (int i=0;i<=fix;i++) if (dp[m][fix-i]!=-1||dp[m][fix+i]!=-1) {k=i;break;} //printf("%d ",k); div=dp[m][fix-k]>dp[m][fix+k]?(fix-k):(fix+k); printf("Jury #%d ",++time); printf("Best jury has value %d for prosecution and value %d for defence: ",(dp[m][div]+div-fix)/2,(dp[m][div]-div+fix)/2); int r=div; for (int i=1;i<=m;i++) { id[i]=d[m-i+1][r]; r-=a[id[i]]-b[id[i]]; } sort(id+1,id+1+m); for (int i=1;i<=m;i++) printf(" %d",id[i]); printf(" "); } return 0; }