这道题应该来说,比较难,特别是变量的下标 有点多 且比较复杂。
题目的意思是:
给出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; }