题意:从 n个人里面找到m个人 每个人有两个值 d p 满足在abs(sum(d)-sum(p)) 最小的前提下sum(d)+sum(p)最大
思路:dp[i][j] i个人中 和是 j 运用背包的思想 二维背包 i是人数容量,人数要符合背包思想,每次只插入一个,逆序枚举
j是sum(d)+sum(p)
注意:这题的标准解法有误:https://blog.****.net/lyy289065406/article/details/6671105 这是有误的解法
错误由 POJ dicuss 提出:
也就是说 如果 存在和1 3 5 <2 4 6 但是差值相同 但是1 3 5 6是最大的 然而这时候 dp[3][j]的路径是2 4 6的路径 就不能再选出 6来更新1 3 5 所以就会有后效性 dp不成立
正解参考:https://blog.****.net/glqac/article/details/22687243
正解运用了背包的思想 进行二维化 第一维表示背包人数容量 第二维表示和 这样能不重不漏把所有情况都枚举了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=;
int dp[][maxn],sub[],Plus[];
vector<int>path[][maxn];
int main(){
int n,m,kase=;
while(scanf("%d%d",&n,&m)==){
if(n+m==)break;
for(int i=;i<m;i++){
for(int j=;j<maxn;j++){
path[i][j].clear();
}
}
memset(dp,-,sizeof(dp));
int a,b;
for(int i=;i<n;i++){
scanf("%d%d",&a,&b);
sub[i]=a-b;
Plus[i]=a+b;
}
int fix=*m;
dp[][fix]=;
for(int k=;k<n;k++){
for(int i=m-;i>=;i--){
for(int j=;j<fix*;j++){
if(dp[i][j]>=){
if(dp[i+][j+sub[k]]<dp[i][j]+Plus[k]){
dp[i+][j+sub[k]]=dp[i][j]+Plus[k];
path[i+][j+sub[k]]=path[i][j];
path[i+][j+sub[k]].push_back(k);
}
}
}
}
}
int i;
for( i=;dp[m][fix-i]==-&&dp[m][fix+i]==-;i++);
int temp=dp[m][fix+i]>dp[m][fix-i]?i:-i;
int sumD = ( dp[m][fix+temp] + temp )/;
int sumP = ( dp[m][fix+temp] - temp )/;
//辩方总值 = (辨控和+辨控差+修正值)/2
//控方总值 = (辨控和-辨控差+修正值)/2
printf( "Jury #%d\n", kase++ );
printf( "Best jury has value %d for prosecution and value %d for defence:\n", sumD,sumP);
for( i=; i < m; i++ )
printf( " %d", path[m][fix+temp][i]+);
printf("\n\n"); }
return ;
}