洛谷 P1064 金明的预算方案(01背包问题)

传送门:Problem 1064

 https://www.cnblogs.com/violet-acmer/p/9852294.html

题解:

  这道题是 “01”背包问题的变形。

  如果不考虑买附件必须买相应的主件这一条件下,这就是单纯的 “01”背包问题。

  那,这道题该如何做呢?

  注意看一下题干,每个主件最多有 2 个附件,那这就容易些了,枚举所有的可能;

  对于第 i 个主件,有以下五种可能:

  (1):不选主件 i 

  (2):只选主件 i

  (3):选主件 i + 附件1

  (4):选主件 i + 附件2

  (5):选主件 i + 附件1 + 附件2

  从这五个中找到最大的价值组合,并和 dp[i-1][ j ]判断,取最大值,当然在选择附件时,需要满足当前价值可以容下所选择的总价值。

AC代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 #define P pair<int ,int >
 5 const int maxn=3.2e4+50;
 6 
 7 int n,m;
 8 int dp[70][maxn];
 9 P mainG[maxn];//存储主件信息
10 P touchG[70][maxn];//touchG[i] : 存储主件 i 含有的附件信息
11 int total[70];//total[i] : 主件 i 含有的附件个数
12 int Val(P p){
13     return p.first*p.second;
14 }
15 void Solve()
16 {
17     for(int i=1;i <= m;++i)
18     {
19         P one=touchG[i][1];
20         P two=touchG[i][2];
21         for(int j=1;j <= n;++j)
22         {
23             dp[i][j]=dp[i-1][j];
24             if(j >= mainG[i].first)
25                 dp[i][j]=max(dp[i][j],dp[i-1][j-mainG[i].first]+Val(mainG[i]));
26             if(j >= mainG[i].first+one.first)
27                 dp[i][j]=max(dp[i][j],dp[i-1][j-mainG[i].first-one.first]+Val(mainG[i])+Val(one));
28             if(j >= mainG[i].first+two.first)
29                 dp[i][j]=max(dp[i][j],dp[i-1][j-mainG[i].first-two.first]+Val(mainG[i])+Val(two));
30             if(j >= mainG[i].first+one.first+two.first)
31                 dp[i][j]=max(dp[i][j],dp[i-1][j-mainG[i].first-one.first-two.first]+Val(mainG[i])+Val(one)+Val(two));
32         }
33     }
34     printf("%d\n",dp[m][n]);
35 }
36 int main()
37 {
38     scanf("%d%d",&n,&m);
39     for(int i=1;i <= m;++i)
40     {
41         int v,p,q;
42         scanf("%d%d%d",&v,&p,&q);
43         if(!q)
44             mainG[i]=P(v,p);
45         else
46             touchG[q][++total[q]]=P(v,p);
47     }
48     Solve();
49 }
二维dp
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 #define P pair<int ,int >
 5 const int maxn=3.2e4+50;
 6 
 7 int n,m;
 8 int dp[maxn];
 9 P mainG[maxn];//存储主件信息
10 P touchG[70][maxn];//touchG[i] : 存储主件 i 含有的附件信息
11 int total[70];//total[i] : 主件 i 含有的附件个数
12 int Val(P p){
13     return p.first*p.second;
14 }
15 void Solve()
16 {
17     for(int i=1;i <= m;++i)
18     {
19         P one=touchG[i][1];
20         P two=touchG[i][2];
21         for(int j=n;mainG[i].first != 0 && j >= 1;--j)
22         {
23             if(j >= mainG[i].first)
24                 dp[j]=max(dp[j],dp[j-mainG[i].first]+Val(mainG[i]));
25             if(j >= mainG[i].first+one.first)
26                 dp[j]=max(dp[j],dp[j-mainG[i].first-one.first]+Val(mainG[i])+Val(one));
27             if(j >= mainG[i].first+two.first)
28                 dp[j]=max(dp[j],dp[j-mainG[i].first-two.first]+Val(mainG[i])+Val(two));
29             if(j >= mainG[i].first+one.first+two.first)
30                 dp[j]=max(dp[j],dp[j-mainG[i].first-one.first-two.first]+Val(mainG[i])+Val(one)+Val(two));
31         }
32     }
33     printf("%d\n",dp[n]);
34 }
35 int main()
36 {
37     scanf("%d%d",&n,&m);
38     for(int i=1;i <= m;++i)
39     {
40         int v,p,q;
41         scanf("%d%d%d",&v,&p,&q);
42         if(!q)
43             mainG[i]=P(v,p);
44         else
45             touchG[q][++total[q]]=P(v,p);
46     }
47     Solve();
48 }
一维dp

   关于主件编号问题(踩坑了):

      m个物品,编号为 1~m,而不是按照主件的个数进行编号。

      例如:                

              (正确编号)(错误编号)

        2000 10      

        500 1 0    1    1
        400 4 0    2    2
        300 5 1    3
        400 5 1    4
        200 5 0    5    3
        500 4 5    6
        400 4 0    7    4
        320 2 0    8    5
        410 3 0    9    6
        400 3 5    10

      找这个BUG找了好几个小时,mmp,心累啊.................

      

 

    

posted @ 2018-10-24 18:53  HHHyacinth  阅读(354)  评论(0编辑  收藏  举报