题目分析:
首先我们用f[m,n]表示有m个候选人差值为n的最大的和。解释一下,已知辩护和反辩的值为d[i],j[i],那么差值就是d[i]-j[i],和就是d[i]+j[i].而这里的差值是选了要求的候选人的差值和和。
那么递推式是什么呢?不妨从f[m-1][t]->f[m][n],那么这两个之间的关系是什么呢,对于所有的t+d[i]-j[i]==k的最大的f[m-1][t]+d[i]+j[i].既是f[m][n]=max(f[m-1][t]+d[i]+j[i])当t+d[i]-j[i]==k的时候。注意:差值可能有负数,我们可以将所有的值同时加上一个数。
对于记录那几个候选人,就是通过顺藤摸瓜的方法,这个方法在很多算法中用过。
对于最后一个我们记做f[m][n]=i;怎么找钱一个呢?前一个就是f[m-1][n-d[i]+j[i]].注意结果要求有序,要排序,不知道为什么sort不行,只好用qsort了。
#include<iostream>
#include<cstdio>
#include<stdlib.h>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[21][801],path[21][801];
int D[201],J[201],res[21];
int cmp(const void *a,const void *b)
{
return *(int*)a-*(int*)b;
}
bool select(int a,int b,int i){
while(a>0 && path[a][b]!=i){
b-=D[path[a][b]]-J[path[a][b]];
a--;
}
return (a!=0)?true:false;//i有没有被用过。
}
int main()
{
int i,j,k,r,n,m,cnt=1,a,b;
while(scanf("%d%d",&n,&m),n||m)
{
for(i=1;i<=n;i++)
{
scanf("%d%d",&D[i],&J[i]);
}
memset(dp,-1,sizeof(dp));
memset(path,0,sizeof(path));
r=m*20;
dp[0][r]=0;
for(j=0;j<m;j++)
{
for(k=0;k<=r*2;k++)
{
if(dp[j][k]>=0)
{
for(i=1;i<=n;i++)
{
if(dp[j+1][k+D[i]-J[i]]<dp[j][k]+D[i]+J[i])//状态方程
{
a=j;
b=k;
if(!select(a,b,i))
{
dp[j+1][k+D[i]-J[i]]=dp[j][k]+D[i]+J[i];
path[j+1][k+D[i]-J[i]]=i;
}
}
}
}
}
}
for(i=r,j=0;dp[m][i+j]<0&&dp[m][i-j]<0;j++);//寻找绝对值最小的
k=dp[m][i+j]>dp[m][i-j]?i+j:i-j;//寻找绝对值最小的和最大的
printf("Jury #%d/n",cnt++);
printf("Best jury has value %d for prosecution and value %d for defence:/n",(dp[m][k]+k-r)/2, (dp[m][k]-k+r)/2);
for(i=1;i<=m;i++)
{
res[i]=path[m-i+1][k];
k-=D[res[i]]-J[res[i]];
}
qsort(res+1,m,sizeof(res[0]),cmp);
for(i=1;i<=m;i++)
printf(" %d",res[i]);
printf("/n/n");
}
return 0;
}