Description
在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定。陪审团是由法官从公众中挑选的。先随机挑选n 个人作为陪审团的候选人,然后再从这n 个人中选m 人组成陪审团。选m 人的办法是:控方和辩方会根据对候选人的喜欢程度,给所有候选人打分,分值从0 到20。为了公平起见,法官选出陪审团的原则是:选出的m 个人,必须满足辩方总分D和控方总分P的差的绝对值|D-P|最小。如果有多种选择方案的 |D-P| 值相同,那么选辩控双方总分之和D+P最大的方案即可。
Output
选取符合条件的最优m个候选人后,要求输出这m个人的辩方总值D和控方总值P,并升序输出他们的编号。
Solution
dp 比较好想,难的是记录路径。
(来自颜神犇的奇技淫巧)
定义路径数组 path[i][j][k] 表示经过 i 个人,选了 j 个人,差值为 k 的状态最后一个选的是谁。
转移的时候让 path[i][j][k]=path[i-1][j][k] 就好
最后输出的时候要这样
int nnow=n; for(int k=m;k;k--){ int now=per[nnow][k][idx]; a+=p[now]; b+=d[now]; idx-=cha[now]; nnow=now-1; // 这里要减一是因为转移的时候是从 i-1 转移来的 ans[++ans[0]]=now; }
Code
// By YoungNeal #include<cstdio> #include<cstring> #include<algorithm> int n,m,T; int ans[805]; int cha[205]; int f[25][1005]; int p[205],d[205]; int per[205][25][1005]; void clear(){ memset(p,0,sizeof p); memset(d,0,sizeof d); memset(f,0xcf,sizeof f); memset(ans,0,sizeof ans); memset(cha,0,sizeof cha); memset(per,0,sizeof per); } signed main(){ while(scanf("%d%d",&n,&m)){ if(n+m==0) return 0; clear(); T++; for(int i=1;i<=n;i++){ scanf("%d%d",&p[i],&d[i]); cha[i]=p[i]-d[i]; } f[0][400]=0; //per[0][0][400]=1; for(int i=1;i<=n;i++){ for(int k=m;k;k--){ for(int j=800;j>=cha[i];j--){ per[i][k][j]=per[i-1][k][j]; if(f[k-1][j-cha[i]]<0) continue; if(f[k][j]<f[k-1][j-cha[i]]+d[i]+p[i]){ f[k][j]=f[k-1][j-cha[i]]+d[i]+p[i]; per[i][k][j]=i; } } } } int idx; for(int i=0;i<=400;i++){ if(f[m][400+i]!=0xcfcfcfcf||f[m][400-i]!=0xcfcfcfcf){ idx=(f[m][400+i]>=f[m][400-i]?400+i:400-i); break; } } int a=0,b=0;ans[0]=0; int nnow=n; for(int k=m;k;k--){ int now=per[nnow][k][idx]; a+=p[now]; b+=d[now]; idx-=cha[now]; nnow=now-1; ans[++ans[0]]=now; } printf("Jury #%d Best jury has value %d for prosecution and value %d for defence: ",T,a,b); std::sort(ans+1,ans+1+ans[0]); for(int i=1;i<=ans[0];i++) printf(" %d",ans[i]);printf(" "); puts(""),puts(""); } }