poj 1015 动态规划
题意:从 n (1<=n<=200)个候选人中选 m (1<=m<=20)人组成陪审团。控方和辩方给所有候选人打分D[i]和P[i],分值从0 到20。
为了公平起见,法官选出陪审团的原则是:选出的m 个人,必须满足辩方总分D和控方总分P的差的绝对值|D-P|最小。如果有多种选择方案的 |D-P| 值相同,那么选辩控双方总分之和D+P最大的方案。
分析:用dp(i,j)表示取i个候选人,差为j的总和。则转移函数为 dp(i, j) = max dp(i-1, j-Vt), (t在i-1个人中没被选到,Vt=Dt-Pt)。
此题要记录选了哪些人,用path[i][j]表示dp[i][j]最后选的那个人的编号,假设为t,则倒数第二个人为 path[i-1][j-Vt]。
另外,由于差值 j 可能为负,所以一律加上400(最多20人,每人最小-20分,所以最小为-400)。
const int N = 202, M = 22, K = 802, fix = 400; int n, m, ii, jj, T; int p[N], d[N], v[N], s[N];//v差 s和 int dp[M][K], path[M][K];//dp[i][j]选i个人,差为j的最大和 path[i][j]为第i个人的编号 int b[N]; void check(int i, int j){ FOE(t, 1, n) b[t] = 0; while(i){ int t = path[i][j]; b[t] = 1; j = j - v[t]; i--; } } void read(){ FOE(i, 1, n) { scanf("%d%d", &p[i], &d[i]); v[i] = d[i]-p[i]; s[i] = p[i]+d[i]; } ii = jj = -1; } void DP(){ memset(dp, -1, sizeof dp); dp[0][fix] = 0; FOR(i, 0, m) FOE(j, 0, 800){ if(dp[i][j] == -1) continue; check(i, j);//找出dp[i][j]没选的人 FOE(t, 1, n){ if(b[t]) continue; int x = i +1, y = j + v[t];//更新d[i+1][j+v[t]] if(dp[x][y] == -1 || dp[x][y] < dp[i][j] + s[t]) { dp[x][y] = dp[i][j] + s[t]; path[x][y] = t; if(x==m) {//更新最优解 int t1 = abs(jj-fix), t2 = abs(y-fix); if(jj==-1 || t1>t2 || (t1==t2 && dp[ii][jj]<dp[x][y])){ ii = x; jj = y; } } } } } } void print(){ int dd = (dp[ii][jj] + jj - fix)/2; int pp = dp[ii][jj] - dd; FOE(t, 1, n) b[t] = 0; while(ii){ int t = path[ii][jj]; b[t] = 1; jj = jj - v[t]; ii--; } printf("Jury #%d \n", ++T); printf("Best jury has value %d for prosecution and value %d for defence: \n", pp, dd); FOE(t, 1, n) if(b[t]) printf(" %d", t); printf("\n"); } int main(){ #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); #endif while(scanf("%d%d", &n, &m), n){ read(); DP(); print(); } return 0; }