[poj1015]Jury Compromise[DP]
题意:$n$个物品选$m$个,每个物品有两个权值$A[i]$和$B[i]$,要求选出的$m$个物品$\left|\sum A[i]-\sum B[i]\right|$最小,当这个差值相同时,选择$\sum A[i]+\sum B[i]$最大的,输出$\sum A[i]$,$\sum B[i]$,方案。
题解:
令$V=\left|\sum A[i]-\sum B[i]\right|$
$S=\sum A[i]+\sum B[i]$
$s[i]=A[i]+B[i]$
$v[i]=A[i]-B[i]$
设$f[j][k]$表示选过第$j$个后 $k=\sum A[i]-\sum B[i]$(注意没有绝对值) 时$S$的最大值,若$f[j][k]==-1$表示不存在合法方案。
$path[j][k]$表示$f[j][k]$是由$f[j-1][k]$选择了$path[j][k]$后得到的,即状态$f[j][k]$处最后选择的物品。
最后方案的输出用$path$数组向前回溯,考虑$path[j][k]$的前一个选的是$path[j-1][k-v[path[j][k]]]$。
转移如下:
if (f[j-1][k]+s[i] > f[j][k+v[i]] && !Selected(j-1,k,i) )//i没有被之前的状态选择
f[j][k+v[i]]=f[j-1][k]+s[i], path[j][k+v[i]]=i;
这样最终答案是$\left| k \right|$最小的合法$f[m][k](f[m][k]!=-1)$。
因为$f[m][k]=\sum A[i]+\sum B[i]$
$k=\sum A[i]-\sum B[i]$
所以答案的$\sum A[i]=(f[m][k]+k)/2$
$\sum B[i]=(f[m][k]-k)/2$
最后,方案的输出用$path$数组向前回溯,考虑$path[j][k]$的前一个选的是$path[j-1][k-v[path[j][k]]]$。
将得到的方案排序输出即可。 为了避免负数下表,要将第二维平移,需注意细节。
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <cstring> 6 #include <ctime> 7 #include <cmath> 8 #include <vector> 9 10 using namespace std; 11 12 int n,m,a[210],b[210],v[210],s[210],fix,Kase; 13 int f[1100][1100],path[1100][1100]; 14 15 bool Selected(int j,int k,const int i) 16 { 17 while(j>0 && path[j][k]!=i) 18 { k-=v[path[j][k]]; j--; } 19 return j; 20 } 21 22 int main() 23 { 24 while (~scanf("%d%d",&n,&m) && n && m) 25 { 26 for (int i=1;i<=n;++i) 27 scanf("%d%d",&a[i],&b[i]), 28 v[i]=a[i]-b[i],s[i]=a[i]+b[i]; 29 30 fix=m*20; memset(f,0xff,sizeof(f)); f[0][fix]=0; 31 32 for (int j=1;j<=m;++j) for (int k=0;k<=fix<<1;++k) 33 { 34 if (f[j-1][k]<0)continue; 35 for (int i=1;i<=n;++i) 36 { 37 if (f[j-1][k]+s[i] > f[j][k+v[i]] && 38 !Selected(j-1,k,i) ) 39 { 40 f[j][k+v[i]]=f[j-1][k]+s[i]; 41 path[j][k+v[i]]=i; 42 } } } 43 44 int tt; for(int k=0;k<=fix;++k) 45 if(f[m][fix-k]>=0 || f[m][fix+k]>=0) { tt=k; break; } 46 47 int temp=(f[m][fix-tt]>f[m][fix+tt]?(fix-tt):(fix+tt)); 48 printf("Jury #%d\nBest jury has value %d for prosecution and" 49 " value %d for defence: \n",++Kase, 50 (f[m][temp]+temp-fix)>>1,(f[m][temp]-temp+fix)>>1); 51 52 vector<int> vec; tt=temp; 53 for(int j=m;j;--j) 54 { vec.push_back(path[j][tt]); tt-=v[vec.back()]; } 55 56 sort(vec.begin(),vec.end()); 57 for(int i=0;i<(int)vec.size();++i) printf(" %d",vec[i]); 58 59 printf("\n"); 60 } 61 return 0; 62 }