zoukankan      html  css  js  c++  java
  • 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
    ",cas++);
    52         printf("Best jury has value %d for prosecution and value %d for defence:
    ",P,D);
    53         for(int i=1;i<=m;i++)
    54             printf(" %d",res[i]);
    55         printf("
    
    ");
    56     }
    57     return 0;
    58 }
    View Code

     正解:

    把循环次序改成“选哪个人->选的第几个人->差值之和”,并且使用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
    ",cas++);
    41         printf("Best jury has value %d for prosecution and value %d for defence:
    ",P,D);
    42         for(int i=0;i<m;++i)
    43             printf(" %d",path[m][S][i]);
    44         printf("
    
    ");        
    45     }
    46     return 0;
    47 }
  • 相关阅读:
    DeviceIOControl读写硬盘设备
    #ifdef的用法
    更改Visual Studio 2010的主题设置[.vssettings格式]
    vc2010 vs2010 智能插件Visual Assist 安装,设置
    VS2010 C++ 操作Excel表格的编程实现
    Python 字符串
    配置opencv2.4.11生成release版本
    配置opencv2.411调试版本(debug)
    边沿检测与提取,轮廓跟踪
    CComboBox控件的使用 1
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/10422233.html
Copyright © 2011-2022 走看看