zoukankan      html  css  js  c++  java
  • JOISC2014 挂饰("01"背包)

    传送门:

      [1]:洛谷

      [2]:BZOJ

    参考资料:

      [1]:追忆:往昔 

    题解

      上述参考资料的讲解清晰易懂,下面谈谈我的理解;

      关键语句:

      

      将此题转化为 "01背包" 类问题,关键就是上述语句;

      据此,定义 dp[ i ][ j ] 表示前 i 个物品在钩子剩余 j 个的状态下所获得的最大喜悦值;

      细节处理:

      

      为了应对负数的情况,让 dp[ i ][ j ] 的下标 j 全部增加 2000 ,这样就可以表示在负数范围内的值了。

      状态转移:

      首先,初始化 dp[][] 数组为 -INF,并令 dp[0][2001]=0(初始手机上含有一个挂钩);

     1 for(int i=1;i <= n;++i)
     2 {
     3     a[i]--;
     4     for(int j=1;j <= 4000;++j)
     5     {
     6         dp[i][j]=max(dp[i][j],dp[i-1][j]);
     7         if(dp[i][j] == -INF)
     8             continue;
     9 
    10         int k=min(j+a[i],4000);
    11         dp[i][k]=max(dp[i][k],max(dp[i-1][j]+b[i],dp[i-1][k]));
    12     }
    13 }    

      解释1:挂饰 i 可以提供 ai 个挂钩,但是,要把它挂到手机上需要消耗一个挂钩,所以,挂饰 i 额外提供 ai-1 个挂钩;

      解释2:第6行的更新是必不可少的,手动测试如下样例便可明白:

    5
    0 0
    0 0
    0 0
    0 0
    0 10

      解释3:第 11 行,dp[ i ][ k ] 只受状态 dp[ i-1 ][ j ] 和 dp[ i-1 ][ k ] 的影响,为什么在判断的时候需要额外与 dp[ i ][ k ] 判断呢?

           仔细看一下 k,如果 j+a[ i ] 超过上界 4000,那么 k = 4000,对于超过 4000 的肯定都更新到了 dp[ i ][ 4000 ]上;

           所以,dp[ i ][ 4000 ] 会首其他状态的影响,而不只是上述两种状态的影响;

      验证解释3的正确性:

      将第 11 行代码改成如下所示代码(AC):

    1 if(k < 4000)
    2     dp[i][k]=max(dp[i-1][j]+b[i],dp[i-1][k]);
    3 else
    4     dp[i][k]=max(dp[i][k],max(dp[i-1][j]+b[i],dp[i-1][k]));

      而改成如下所示代码(WA):

    1 dp[i][k]=max(dp[i-1][j]+b[i],dp[i-1][k]);

    •Code

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define INF 0x3f3f3f3f
     4 #define mem(a,b) memset(a,b,sizeof(a))
     5 const int maxn=2e3+50;
     6 
     7 int n;
     8 int a[maxn];
     9 int b[maxn];
    10 int dp[maxn][maxn<<1];
    11 
    12 int Solve()
    13 {
    14     for(int i=0;i <= n;++i)
    15         for(int j=0;j <= 4000;++j)
    16             dp[i][j]=-INF;
    17 
    18     dp[0][2001]=0;
    19     for(int i=1;i <= n;++i)
    20     {
    21         a[i]--;///物品i本身需要占用一个钩子,所以其可以提供的钩子个数为a[i]-1
    22         for(int j=1;j <= 4000;++j)
    23         {
    24             dp[i][j]=max(dp[i][j],dp[i-1][j]);
    25             
    26             if(dp[i][j] == -INF)
    27                 continue;
    28 
    29             int k=min(j+a[i],4000);
    30             dp[i][k]=max(dp[i][k],max(dp[i-1][j]+b[i],dp[i-1][k]));
    31         }
    32     }
    33     int ans=0;
    34     for(int i=2000;i <= 4000;++i)
    35         ans=max(ans,dp[n][i]);
    36     return ans;
    37 }
    38 
    39 int main()
    40 {
    41 //    freopen("C:\Users\hyacinthLJP\Desktop\in&&out\contest","r",stdin);
    42     scanf("%d",&n);
    43     for(int i=1;i <= n;++i)
    44         scanf("%d%d",a+i,b+i);
    45 
    46     printf("%d
    ",Solve());
    47 
    48     return 0;
    49 }
    二维dp

    •利用滚动数组降低dp的维数

      

    •Code

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define INF 0x3f3f3f3f
     4 #define mem(a,b) memset(a,b,sizeof(a))
     5 const int maxn=2e3+50;
     6 
     7 int n;
     8 int a[maxn],b[maxn];
     9 int dp[maxn<<1];
    10 
    11 int Solve()
    12 {
    13     mem(dp,-INF);
    14     dp[2001]=0;
    15 
    16     for(int i=1;i <= n;++i)
    17     {
    18         a[i]--;
    19         if(a[i] > 0)
    20         {
    21             for(int j=4000;j >= 0;--j)
    22             {
    23                 if(dp[j] == -INF)
    24                     continue;
    25                 int k=min(j+a[i],4000);
    26                 dp[k]=max(dp[j]+b[i],dp[k]);
    27             }
    28         }
    29         else
    30         {
    31             for(int j=0;j <= 4000;++j)
    32             {
    33                 if(dp[j] == -INF)
    34                     continue;
    35                 dp[j+a[i]]=max(dp[j]+b[i],dp[j+a[i]]);
    36             }
    37         }
    38     }
    39     int ans=0;
    40     for(int i=2000;i <= 4000;++i)
    41         ans=max(ans,dp[i]);
    42 
    43     return ans;
    44 }
    45 int main()
    46 {
    47     scanf("%d",&n);
    48     for(int i=1;i <= n;++i)
    49         scanf("%d%d",a+i,b+i);
    50 
    51     printf("%d
    ",Solve());
    52 
    53     return 0;
    54 }
    一维dp(滚动数组)
  • 相关阅读:
    JavaSE 基础
    Mybatis(三) 动态SQL
    Java基础(十)
    Java基础(九)
    Java基础(八)
    Java基础(七)
    centos7怎么把语言切换成英语
    CentOS更改yum源与更新系统
    快速切換前後日期
    laravel 框架给数组分页
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/11205582.html
Copyright © 2011-2022 走看看