https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=259
题目
在一个遥远的国家,法庭上审判结果需要陪审团给出,陪审团是从大众中选出来的。首先从社会中随机抽取n个人,参与诉讼的控方和辩方会对每个人打分,0表示完全不赞同,20表示完全赞同。法官为了公平,会尽量均衡地从这n个人中选择m个人,使控方和辩方都比较满意。具体是:将选出的m个人的控方打分加起来,得到a,将选出的m个人的检方打分加起来,得到b,计算$leftlvert a-b ight vert$。选择$leftlvert a-b ight vert$最小的m人作为陪审团,如果同时存在多种选择,选择a+b最大的作为陪审团。写出程序,模拟选择的过程,并输出选择的方案。
$1leqslant nleqslant 200, 1leqslant mleqslant 20$
题解
设$dp[a][i][j]$为前$a$个人选择了i个人,差为j
$dp[a][i][j]=dp[a-1][i][j]$
$dp[a][i][j]=dp[a-1][i-1][j-(p-q)]+a[p]+a[q]$
计算值的时候可以滚动优化掉第一维,转移的时候需要完整记录
AC代码
#include<cstdio> #include<cstring> #define REP(i,a,b) for(register int i=(a); i<(b); i++) #define REPE(i,a,b) for(register int i=(a); i<=(b); i++) #define PERE(i,a,b) for(register int i=(a); i>=(b); i--) using namespace std; typedef long long ll; int dp[27][807]; int pj[207][27][807], pa[207][27][807]; bool can[27][807]; int p[207], d[207]; int n,m; void prn(int a, int i, int j) { if(i==0) return; int ka = pa[a][i][j]; prn(ka-1, i-1, pj[ka][i][j]); printf(" %d", ka); } int main() { int kase=0; while(~scanf("%d%d", &n, &m) && n) { REPE(i,1,n) { scanf("%d%d", &p[i], &d[i]); } memset(can,0,sizeof can), memset(dp,-1,sizeof dp); dp[0][400]=0, can[0][400]=1; //dp[a][i][j] = dp[a-1][i][j] //dp[a][i][j] = dp[a-1][i-1][j-(p[i]-d[i])] REPE(a,1,n) PERE(i,m,1) { REPE(j,0,800) { int pv=j-(p[a]-d[a]); pa[a][i][j]=pa[a-1][i][j]; pj[a][i][j]=pj[a-1][i][j]; if(pv<=800 && pv>=0 && can[i-1][pv]) { int nd=dp[i-1][pv]+p[a]+d[a]; if(nd>dp[i][j]) { dp[i][j]=nd; can[i][j]=1; pj[a][i][j]=pv; pa[a][i][j]=a; } } } } int ans=-1, ch=-1; REPE(dj,0,400) { if(can[m][dj+400] && dp[m][dj+400]>ans) { ans=dp[m][dj+400]; ch=dj+400; } if(can[m][400-dj] && dp[m][400-dj]>ans) { ans=dp[m][400-dj]; ch=400-dj; } if(ch>=0) break; } printf("Jury #%d ", ++kase); printf("Best jury has value %d for prosecution and value %d for defence: ", (ans+ch-400)/2, (ans-ch+400)/2); prn(n, m, ch); putchar(' '); putchar(' '); } }