大致题意:每个人有一个p和d值,现在有n个人,求取m个人,使m个人的 p之和 与 d之和 的差的绝对值最小,如果有相等的情况下取使p与d的总和最大的情况。问该哪些人
思路:二维dp
dp[i][j]表示的是 取i个人 这i个人pd差为j时的pd和。
遍历每一个人 成为第m~1个选中的人的情况(按序选择, 小的人先选)
如果我们用数据代入下面代码会发现,当枚举第一个人的情况的时候(i==1)它只能在j=1的情况下进行判断是否更新数据(保证了第一个人要么不选,要么就成为第一个被选的
同理,枚举第二个人的时候,要么不选,要么就成为第一个被选的,要么就成为第二个被选的。
(确保了有序)
代码:
1 #include <iostream> 2 #include <stack> 3 using namespace std; 4 typedef long long ll; 5 const int mx=2e2+10; 6 const int inf=-(1<<30); 7 typedef struct NODE{ 8 ll sum, ded; 9 }NODE; 10 NODE nos[mx]; 11 ll n, m, T, base; 12 ll pre[mx][mx]; 13 ll dp[mx][mx];//dp[i][j]表示在选了i人的情况下使辩控差为j的最大的辩控和的值 14 int main(){ 15 while(scanf("%lld %lld", &n, &m)!=EOF){ 16 if(0==n&&0==m) break; 17 for(ll i=1;i<=n;i++){ 18 ll a, b; 19 scanf("%lld %lld", &a, &b); 20 nos[i].sum=a+b, nos[i].ded=a-b; 21 } 22 base=20*m; 23 for(ll i=0;i<=m;i++) 24 for(ll j=0;j<=base*2;j++) 25 dp[i][j]=pre[i][j]=inf;//初始化 26 dp[0][base]=0; 27 for(ll i=1;i<=n;i++){//选一个人 28 for(ll j=m;j>=1;j--){//选了多少人 要从大到小 只有从大到小才能避免同一个人选多次的情况 29 for(ll k=0;k<=2*base;k++){//辩控差 30 if(dp[j-1][k]==inf) continue; 31 if(dp[j][k+nos[i].ded]<dp[j-1][k]+nos[i].sum){ 32 dp[j][k+nos[i].ded]=dp[j-1][k]+nos[i].sum; 33 pre[j][k+nos[i].ded]=i; 34 } 35 } 36 } 37 } 38 ll d=0; 39 for(;dp[m][base+d]==inf&&dp[m][base-d]==inf;d++); 40 d=dp[m][base+d]==inf?base-d:base+d; 41 ll sumpro=0, sumdef=0; 42 stack<ll>s; 43 while(pre[m][d]!=inf){ 44 ll id=pre[m][d]; 45 s.push(id); 46 sumpro+=(nos[id].sum+nos[id].ded)/2, sumdef+=(nos[id].sum-nos[id].ded)/2; 47 m--, d-=nos[id].ded; 48 } 49 printf("Jury #%lld \nBest jury has value %lld for prosecution and value %lld for defence: \n", 50 ++T, sumpro, sumdef); 51 while(!s.empty()){ 52 printf(" %lld", s.top()); 53 s.pop(); 54 } 55 } 56 return 0; 57 }