poj1015 正解--二维DP(完全背包)

题目链接:http://poj.org/problem?id=1015

错误解法:

网上很多解法是错误的,用dp[i][j]表示选择i个人差值为j的最优解,用path[i][j]存储路径,循环次序为“选的第几个人->选哪个人->差值之和”或者“选的第几个人->差值之和->选哪个人”,为了避免选择重复的人需要判断。错误的原因是存储路径的方式使得会覆盖一些情况,比如1 3 5和2 4 6均满足dp[3][k]最优时,若采用2 4 6作为dp[3][k]的最优解,而1 3 5 6是最终答案,那么此时6已经被dp[3][k]选择了,则得不到最终答案。

比如这组数据:

9 6
6 2
16 10
4 9
19 8
17 12
4 7
10 2
2 14
5 18
0 0
这组数据的正确答案是
Jury #1
Best jury has value 54 for prosecution and value 54 for defence:
1 2 3 4 6 9
但是错误程序的答案是
Jury #1
Best jury has value 52 for prosecution and value 52 for defence:
 1 3 4 5 6 8
但由于poj这题的数据较弱,故这种解法也可以AC,uva323那道的数据较强,就会卡这种做法,错误解的代码如下:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cmath>
 5 using namespace std;
 6
 7 int n,m,p,d,cas=1,fix,S,A,D,P;
 8 int dp[25][900],path[25][900],sub[205],add[205],res[25];
 9
10 bool is(int i,int j,int k){
11     while(i>0){
12         if(path[i][j]==k)
13             return false;
14         j=j-sub[path[i][j]];i=i-1;
15     }
16     return true;
17 }
18
19 int main(){
20     while(scanf("%d%d",&n,&m)!=EOF&&n){
21         memset(dp,-1,sizeof(dp));
22         memset(path,0,sizeof(path));
23         fix=20*m;
24         dp[0][fix]=0;
25         for(int i=1;i<=n;i++){
26             scanf("%d%d",&p,&d);
27             sub[i]=p-d;
28             add[i]=p+d;
29         }
30         for(int i=0;i<m;i++)
31             for(int j=0;j<=2*fix;j++)
32                 if(dp[i][j]>=0)
33                     for(int k=1;k<=n;k++)
34                         if(is(i,j,k)&&dp[i][j]+add[k]>dp[i+1][j+sub[k]]){
35                             dp[i+1][j+sub[k]]=dp[i][j]+add[k];
36                             path[i+1][j+sub[k]]=k;
37                         }
38         int key;
39         for(key=0;key<=fix;key++)
40             if(dp[m][fix+key]>=0||dp[m][fix-key]>=0)
41                 break;
42         S=dp[m][fix+key]>dp[m][fix-key]?fix+key:fix-key;
43         A=dp[m][S];
44         for(int i=m,j=S;i>0;){
45             res[i]=path[i][j];
46             j=j-sub[res[i]];
47             i--;
48         }
49         sort(res+1,res+m+1);
50         P=(A+(S-fix))/2;D=(A-(S-fix))/2;
51         printf("Jury #%d\n",cas++);
52         printf("Best jury has value %d for prosecution and value %d for defence:\n",P,D);
53         for(int i=1;i<=m;i++)
54             printf(" %d",res[i]);
55         printf("\n\n");
56     }
57     return 0;
58 }

正解:

把循环次序改成“选哪个人->选的第几个人->差值之和”,并且使用vector<int> path[25][805]存储路径,从而可以存储所有情况,无法理解的话,就举个例子模拟模拟,而且是由于按照顺序遍历,最后的路径本身就是有序的。因为差值可能为负值,需要加一个修正值fix=m*20。循环部分就是完全背包模型,每个人的重量为1,背包重量为m,从1到n遍历每个人是否被选中,从m-1到0遍历背包的空间(因为每个人最多选一次,故倒序遍历)。详见代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<vector>
 5 using namespace std;
 6
 7 int n,m,cas=1,fix,p,d,S,A,P,D;
 8 int dp[25][805],sub[205],add[205];
 9 vector<int> path[25][805];
10
11 int main(){
12     while(~scanf("%d%d",&n,&m)&&n){
13         memset(dp,-1,sizeof(dp));
14         for(int i=1;i<=m;++i)
15             for(int j=0;j<805;++j)
16                 path[i][j].clear();
17         fix=20*m;
18         dp[0][fix]=0;
19         for(int i=1;i<=n;++i){
20             scanf("%d%d",&p,&d);
21             sub[i]=p-d;
22             add[i]=p+d;
23         }
24         for(int i=1;i<=n;++i)
25             for(int j=m-1;j>=0;--j)
26                 for(int k=0;k<=2*fix;++k)
27                     if(dp[j][k]>=0)
28                         if(dp[j][k]+add[i]>dp[j+1][k+sub[i]]){
29                             dp[j+1][k+sub[i]]=dp[j][k]+add[i];
30                             path[j+1][k+sub[i]]=path[j][k];
31                             path[j+1][k+sub[i]].push_back(i);
32                         }
33         int kk;
34         for(kk=0;kk<=fix;++kk)
35             if(dp[m][fix+kk]>=0||dp[m][fix-kk]>=0)
36                 break;
37         S=dp[m][fix+kk]>dp[m][fix-kk]?fix+kk:fix-kk;
38         A=dp[m][S];
39         P=(A+(S-fix))/2,D=(A-(S-fix))/2;
40         printf("Jury #%d\n",cas++);
41         printf("Best jury has value %d for prosecution and value %d for defence:\n",P,D);
42         for(int i=0;i<m;++i)
43             printf(" %d",path[m][S][i]);
44         printf("\n\n");
45     }
46     return 0;
47 }

原文地址:https://www.cnblogs.com/FrankChen831X/p/10422233.html

时间: 2024-10-17 17:38:50

poj1015 正解--二维DP(完全背包)的相关文章

(hdu)5234 Happy birthday 二维dp+01背包

题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5234 Problem Description Today is Gorwin’s birthday. So her mother want to realize her a wish. Gorwin says that she wants to eat many cakes. Thus, her mother takes her to a cake garden. The garden

二维dp

原题http://acm.hdu.edu.cn/showproblem.php?pid=3127 WHUgirls Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 2050    Accepted Submission(s): 780 Problem Description There are many pretty girls i

Astar_D_二维DP

Labyrinth Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2911    Accepted Submission(s): 1007 Problem Description 度度熊是一只喜欢探险的熊,一次偶然落进了一个m*n矩阵的迷宫,该迷宫只能从矩阵左上角第一个方格开始走,只有走到右上角的第一个格子才算走出迷宫,每一次只能走一格

HDU 4901 The Romantic Hero(二维dp)

题目大意:给你n个数字,然后分成两份,前边的一份里面的元素进行异或,后面的一份里面的元素进行与.分的时候按照给的先后数序取数,后面的里面的所有的元素的下标一定比前面的大.问你有多上种放元素的方法可以使得前面异或的值和后面与的值相等. dp[x][y] 表示走到第x步,得到y这个数字一共有多少种方法. 但是需要注意这里得分一下,不能直接用dp数组存种数,你需要分一下从上一层过来的次数,和这一层自己可以到达的次数.然后取和的时候前后两个集合的种数进行乘法,注意边乘边取余. 顺便给一组数据: 4 3

二维dp(O(N^3)实现) zoj3230

1 #include<iostream> 2 #include<string.h> 3 #include<stdio.h> 4 #define maxn 125 5 using namespace std; 6 7 int cost[maxn][maxn],w[maxn][maxn]; 8 int dp[maxn][maxn]; 9 int N,M; 10 int main(){ 11 while(cin>>N>>M){ 12 if (N==0

hoj_10014_二维DP

The Triangle Time Limit: 1000ms, Special Time Limit:2000ms, Memory Limit:32768KB Total submit users: 952, Accepted users: 860 Problem 10014 : No special judgement Problem description 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 (Figure 1) Figure 1 shows a number tr

XTU1168:Alice and Bob(二维DP)

摘要:Dota(Defence of the Ancients,远古的守护), 是指基于魔兽争霸3:冰封王座(暴雪娱乐公司出品)的多人即时对战自定义地图,可支持10个人同时连线游戏.Dota以对立的两个小队展开对战,通常是5v5,游戏目的是守护自己的远古遗迹(近卫方的生命之树.天灾方的冰封王座),同时摧毁对方的远古遗迹.DotA是目前唯一被暴雪娱乐公司官方认可的魔兽争霸RPG.Dota在大学生中的风靡程度令人咂舌,而随着玩家对游戏的理解深入,本身存在于游戏中的许多数学模型被挖掘出来进行研究.游戏

HDU 5074 Hatsune Miku(简单二维dp)

题目大意:给你一些音符之间的联系,给你一个串,让你求出这个串的最大值.-1的时候可以任意替代,其他情况必须为序列上的数. 解题思路:简单二维dp,分情况处理就可以了啊. Hatsune Miku Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Total Submission(s): 637    Accepted Submission(s): 458 Problem De

(review)zoj4800 二维dp 状态转移很灵活

1 #include<iostream> 2 #include<stdio.h> 3 4 using namespace std; 5 6 double dp[10005][125]; 7 double p[125][125]; 8 int pk[10005]; 9 10 int N,M; 11 12 double fmax(double a,double b){ 13 if(a-b>0) return a;else return b; 14 } 15 int main(){