题目大意:
给定两组数,每组n个,p1...pn,d1..dn,其中pi,di对应于同一个人,要选出m个人,这m个人的Σp - Σd的绝对值最小,当有多组数据符合条件时,选择Σp+Σd的绝对值最大的一组数
从小到大print选择的m个人的编号
思路分析:
这道题的难点在于确定状态转移方程的参量含义,参考了一下网上的解法,
dp[i][j]表示已经选了i个人且 Σp - Σd = j 时 Σp+Σd的值
状态转移方程是
if(dp[i][j] + p[k] + d[k] > dp[i+1][j +p[k] - d[k] && k 不在dp[i][j]已经选过的人中){
dp[i+1][j+p[k] - d[k]] = dp[i][j] + p[k] + d[k];
}
那么怎样判断k是不是在达到dp[i][j]状态时所选择的人中呢?
可以相应的用path[i][j]表示达到dp[i][j]时选择的人,这样逐步回溯就可以找到所有参与形成dp[i][j]的人
解题技巧:
1.由于p[k]-d[k]可能为负数,所以要对结果做整体的偏移, 题目中已经给定1<=m<=20且pk,dk的值都在0-20之间,所以可以
令偏移量为400,避免j出现负数
2.用path[i][j]回溯的方法记录所选择的人,这种方法在floyd算法求任意两点间最短路的时候也有用到
3.从400(偏移量)向两边探测出使得dp[m][k] !=0的值
#include<iostream>
#include<cstdio>
#include<string.h>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int maxn = 205;
const int maxm = 25;
const int offset = 400;
int dp[maxm][(offset<<1)+50];
int path[maxm][(offset<<1)+50];
int n,m;
int d[maxn],p[maxn]; //记录信息
int main(){
/*Sample Input
4 2
1 2
2 3
4 1
6 2
0 0
Sample Output
Jury #1
Best jury has value 6 for prosecution and value 4 for defence:
2 3 */
//freopen("1.txt","r",stdin);
int i,j,k,Ca = 1;
while(scanf("%d%d",&n,&m)!=EOF){
if(n == 0 && m == 0)
break;
for(i = 0; i < n; ++i){
scanf("%d%d",p+i,d+i);
}
/*init*/
memset(dp,-1,sizeof(dp));
memset(path,0,sizeof(path));
int N = m * 20;
dp[0][N] = 0;
/*init*/
//dp[i][j]表示一共有i个人且Σp - Σq = j时的Σp+Σq
int twiceN = N << 1,t1,t2,t3;
for(i = 0; i < m; ++i){
for(j = 1; j <= twiceN; ++j){
if(dp[i][j] >= 0){
for(k = 0; k < n; ++k){
if(dp[i][j] + p[k] + d[k] > dp[i+1][j + p[k] - d[k]]){
t1 = i; t2 = j;
while(t1 > 0 && path[t1][t2] != k){
t3 = path[t1][t2];
t2 -= p[t3] - d[t3];
--t1;
}
if(t1 == 0){
dp[i+1][j + p[k] - d[k]] = dp[i][j] + p[k] + d[k];
path[i+1][j+p[k]-d[k]] = k;
}
}
}
}
}
}
int ty = 0;
while(dp[m][N-ty] < 0 && dp[m][N+ty] < 0)
++ty;
int record;
if(dp[m][N-ty] > dp[m][N+ty]){
record = N - ty;
}
else{
record = N + ty;
}
int pans = (dp[m][record] + record - N) >> 1;
int dans = (dp[m][record] - record + N) >> 1;
printf("Jury #%d\n",Ca++);
printf("Best jury has value %d for prosecution and value %d for defence:\n",pans,dans);
int ans[maxm];
int peek = 0;
/*for(j = path[m][record]; j != 0;){
ans[peek] = j;
j = path[--m][record - p[j] + d[j]];
}*/
/*the above part is incorrect too*/
for(i = 0; i < m; ++i){
ans[peek] = path[m-i][record];
record -= p[ans[peek]] - d[ans[peek]];
++peek;
}
sort(ans,ans+m);
for(j = 0;j < m; ++j){
printf(" %d",ans[j]+1);
}
printf("\n\n");
}
return 0;
}