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('
');
}
}