刚开始路径记录的时候用的状压,结果一直WA,然后对照官方数据,发现m虽然只有20,但n却是有200的
说多了都是泪啊,调了我好久
/*刚开始想了个递推方程,后来发现是不正确的-_-||因为这个方程不能保证无后效性。。。
dp[k][i][j]:前k个人中,选中m个人,sumd-sump的值。
递推方程:dp[k][i][j]=max(dp[k][i-1][j-(d[i]-p[i])]);
重新看了一下背包九讲,发现这次再来看理解又深刻了不少:
这道题中,我们要做的是从100个中选20个出来,求其 |sumd-sump|最小,因为我们这里多了个绝对值,这使得我们如果按原来的思路,就不能保证无后效性。
因此,我们把计算出来的结果作为其一个费用,按问题转化为二维费用的问题,再按照0-1背包处理,就可以了
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int p[205],d[205],n,m;
struct Jury{
int val,path[21];
}dp[25][851];
void init(){
int i,j;
for(i=0;i<=24;i++){
for(j=0;j<=821;j++){
dp[i][j].val=-1;
for(int k=0;k<21;k++) dp[i][j].path[k]=0;
}
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int cas=1;
while(scanf("%d%d",&n,&m)){
if(n==0 && m==0 ) break;
memset(d,0,sizeof(d));
memset(p,0,sizeof(p));
int i,j,k;
for(i=1;i<=n;i++) scanf("%d%d",&p[i],&d[i]);
init();
int fix=20*m;
dp[0][fix].val=0;
for(k=1;k<=n;k++){
for(i=m-1;i>=0;i--){//只能用一次,从后往前
for(j=0;j<2*fix;j++){//到达j分数时的情况,有标志位记录m了,所以不用在乎顺序
int tmp=j+(d[k]-p[k]);
if(dp[i][j].val!=-1) {
if(dp[i][j].val+p[k]+d[k]>dp[i+1][tmp].val) {
dp[i+1][tmp].val=dp[i][j].val+p[k]+d[k];
for(int l=0;l<21;l++) dp[i+1][tmp].path[l]=dp[i][j].path[l];
dp[i+1][tmp].path[i+1]=k;
}
}
}
}
}
printf("Jury #%d\n",cas++);
for(i=0;i<=2*fix;i++){
if(dp[m][fix-i].val!=-1||dp[m][fix+i].val!=-1){
int temp=i;
if(dp[m][fix-i].val>dp[m][fix+i].val) temp=-temp;
int tmp1=(dp[m][fix+temp].val+temp)/2;
int tmp2=(dp[m][fix+temp].val-temp)/2;
printf( "Best jury has value %d for prosecution and value %d for defence:\n", tmp2,tmp1);
for(j=1;j<=20;j++){
if(dp[m][fix+temp].path[j]) printf(" %d",dp[m][fix+temp].path[j]);
}
printf("\n");
break;
}
}
printf("\n");
}
return 0;
}