Jury Compromise POJ - 1015
考察:01背包dp
错误思路:
定义二维结构体结构体DP[i][j],代表前i个人选j个的值.DP[i][j].cost代表最小花费,DP[i][j].sum代表最大和.由第i个人选不选划分集合.
此思路错在这里的最优子结构不一定推得到最优解.
比如数据:
5 3 1 1 2 3 4 4 3 2 2 2 0 0
当我们枚举到f[4][3]时就出现了错误,f[4][3]的由f[3][2]和f[3][3]推来,可以发现f[3][2]取不到使f[4][3]取最优解的值.因为f[3][2]一定会取1,1与4,4.为了保存非当前最优解的子结构,需要再扩展一维.
正确思路:
因此还需要再继续划分集合,以前i个人的累加差再划分,可以发现会出现负数,所以需要偏移.最后枚举最小差,再根据结果逆推回最初状态.
这里如果初始化f[0][0][base*m]可能更方便一点.
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int N = 210,M = 21,K = 810,Base = 20; 6 int f[N][M][K],n,m,ans[M]; 7 typedef pair<int,int> PII; 8 PII p[N]; 9 int main() 10 { 11 int kcase = 0; 12 while(scanf("%d%d",&n,&m)!=EOF&&n) 13 { 14 memset(f,-0x3f,sizeof f); 15 f[0][0][0] = 0; 16 for(int i=1;i<=n;i++) scanf("%d%d",&p[i].first,&p[i].second); 17 if(kcase) printf("\n"); 18 for(int i=1;i<=n;i++) 19 for(int j=0;j<=m&&j<=i;j++) 20 for(int k=0;k<=m*Base*2;k++) 21 { 22 int b = p[i].second-p[i].first+Base,v =0; 23 f[i][j][k] = f[i-1][j][k]; 24 if(k-b<0||k-b>m*Base*2) continue; 25 if(j>=1) f[i][j][k] = max(f[i-1][j-1][k-b]+p[i].first+p[i].second,f[i][j][k]); 26 }//ans差值最小的前提下,和最大. 27 int base = Base*m,k = 0,res;//找最小差值 28 while(f[n][m][base-k]<0&&f[n][m][base+k]<0) k++; 29 if(f[n][m][base-k]>f[n][m][base+k]) res = base-k; 30 else res = base+k; 31 int cnt = m,t = n,w = f[n][m][res],resf = 0,resz = 0; 32 while(cnt) 33 { 34 if(f[t-1][cnt][res]==w) t--; 35 else{ 36 w-=p[t].first+p[t].second; 37 res-=(p[t].second-p[t].first+Base); 38 resz+=p[t].second;//z为辩方 39 resf+=p[t].first; 40 ans[cnt--] = t; 41 t--; 42 } 43 } 44 printf("Jury #%d\n",++kcase); 45 printf("Best jury has value %d for prosecution and value %d for defence:\n",resf,resz); 46 for(int i=1;i<=m;i++) 47 printf(" %d",ans[i]); 48 printf("\n"); 49 } 50 return 0; 51 }
根据偏移方式的不同,计算方法也会不同.这里将所有数值偏移20,相当于把20看作0,eg数据: -7,-10,9. 偏移后-7+-10的和是13+10+20 = 43.上面的偏移方式是计算为13+10=23.实际上还是将0视为真正的0.这只适用于已知选几个人的情况.Y总的方式才是完全的偏移.
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int N = 210,M = 21,K = 810,Base = 20; 6 int f[N][M][K],n,m,ans[M]; 7 typedef pair<int,int> PII; 8 PII p[N]; 9 int main() 10 { 11 int kcase = 0; 12 while(scanf("%d%d",&n,&m)!=EOF&&n) 13 { 14 memset(f,-0x3f,sizeof f); 15 f[0][0][Base*m] = 0; 16 for(int i=1;i<=n;i++) scanf("%d%d",&p[i].first,&p[i].second); 17 if(kcase) printf("\n"); 18 for(int i=1;i<=n;i++) 19 for(int j=0;j<=m&&j<=i;j++) 20 for(int k=0;k<=m*Base*2;k++) 21 { 22 int b = p[i].second-p[i].first,v =0; 23 f[i][j][k] = f[i-1][j][k]; 24 if(k-b<0||k-b>m*Base*2) continue; 25 if(j>=1) f[i][j][k] = max(f[i-1][j-1][k-b]+p[i].first+p[i].second,f[i][j][k]); 26 }//ans差值最小的前提下,和最大. 27 int base = Base*m,k = 0,res;//找最小差值 28 while(f[n][m][base-k]<0&&f[n][m][base+k]<0) k++; 29 if(f[n][m][base-k]>f[n][m][base+k]) res = base-k; 30 else res = base+k; 31 int cnt = m,t = n,w = f[n][m][res],resf = 0,resz = 0; 32 while(cnt) 33 { 34 if(f[t-1][cnt][res]==w) t--; 35 else{ 36 w-=p[t].first+p[t].second; 37 res-=(p[t].second-p[t].first); 38 resz+=p[t].second;//z为辩方 39 resf+=p[t].first; 40 ans[cnt--] = t; 41 t--; 42 } 43 } 44 printf("Jury #%d\n",++kcase); 45 printf("Best jury has value %d for prosecution and value %d for defence:\n",resf,resz); 46 for(int i=1;i<=m;i++) 47 printf(" %d",ans[i]); 48 printf("\n"); 49 } 50 return 0; 51 }