这道题应该来说,比较难,特别是变量的下标 有点多 且比较复杂。
题目的意思是:
给出n组数,每组数有一个P[i],D[i], 求出其中的m组,使得sum(P[i])-sum(D[i])绝对值最大,如果有多个这样的值,取其中sum(P[i])+sum(D[i])最大的一组
分析:算法思想,动态规划
用 V(i) 表示第i组的辩控差
用S(i) 表示第i组的辩控和
用f(j,k) 表示取j个人,辩控差为k的方案的最大的辩控和
Path[i][j]表示最后加进来的那个人
状态转化方程:
f[i+1][j+P[k]-D[k]] = f[i][j] + P[k] + D[k], 要求k之前没有选过
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
int f[30][1000];//f[j][k]表示取j个候选人,辩控差为k的方案中
//辩控和最大的那个方案
int Path[30][1000];
//记录了最后一个候选人的编号
int P[300];
int D[300];
int Answer[30]; //存放最终方案的人选
bool cmp(int a,int b){
if(a<b)
return true;
else
return false;
}
int main(){
int i,j,k;
int t1,t2;
int n,m;
int nMinP_D;
int iCase; //测试编号
iCase = 0;
while(scanf("%d %d",&n,&m)){
if(n==0 && m==0) break;
iCase++;
for(i=1;i<=n;i++){
scanf("%d %d",&P[i],&D[i]);
}
memset(f,-1,sizeof(f));
memset(Path,0,sizeof(Path));
nMinP_D = m*20;
f[0][nMinP_D] = 0;
for(j=0;j<m;j++){
for(k=0;k<=nMinP_D*2;k++){
if(f[j][k]>=0){
for(i=1;i<=n;i++){
if(f[j][k]+P[i]+D[i]>f[j+1][k+P[i]-D[i]]){
t1 = j;
t2 = k;
while(t1>0&&Path[t1][t2]!=i){ //排除掉选过的情况
t2 -= P[Path[t1][t2]] - D[Path[t1][t2]];
t1--;
}
if(t1 == 0){
f[j+1][k+P[i]-D[i]] = f[j][k]+P[i]+D[i];
Path[j+1][k+P[i]-D[i]] = i;
}
}
}
}
}
}
i = nMinP_D;
j = 0;
while(f[m][i+j]<0&&f[m][i-j]<0) j++; //找到辩控差最小的情况
if(f[m][i+j]>f[m][i-j]) k=i+j;
else k=i-j;
printf("Jury #%d\n",iCase);
printf("Best jury has value %d for prosecution and value %d for defence:\n",(k+f[m][k]-nMinP_D)/2,(f[m][k]+nMinP_D-k)/2); //相当于解一个方程
//x+y=f[m][k];x-y+MinP_D=k
for(i=1;i<=m;i++){
Answer[i] = Path[m-i+1][k];
k -= P[Answer[i]] - D[Answer[i]]; //向上回溯解得 选中的 人
}
sort(Answer+1,Answer+m+1,cmp);
for(i=1;i<=m;i++)
printf(" %d",Answer[i]);
printf("\n\n");
}
return 0;
}