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 }
偏移方式

 

posted @ 2021-03-14 17:03  acmloser  阅读(53)  评论(0编辑  收藏  举报