zoukankan      html  css  js  c++  java
  • POJ 3260 多重背包+完全背包

      前几天刚回到家却发现家里没网线 && 路由器都被带走了,无奈之下只好铤而走险尝试蹭隔壁家的WiFi,不试不知道,一试吓一跳,用个手机软件简简单单就连上了,然后在浏览器输入192.168.1.1就能看到他的路由器的一切信息,包括密码,然后打开笔记本……好了,废话不多说,能连上网后第一时间当然是继续和队友之前约好的训练了。

      今天翻看到之前落下的一道混合背包题目,然后在草稿本上慢慢地写递推方程,把一些细节细心地写好…(本来不用太费时间的,可是在家嘛,一会儿妈走来要我教她玩手机,一会儿有一个亲戚朋友来……简直无语了,程序猿最讨厌被人打断的!虽说我不是码农)然后,这道题,细心分析后,发现它是多重背包+完全背包的。

      首先,可以先顺着推出 John手中的coin能拼凑出的币值的最小币数,即设dp[i][j]为前 i 种coin凑出币值 j 时需要的最小币数,很明显,dp[i][j]= min(dp[i-1][j], dp[i][j-coin[i]]+1) 的递推方程很容易想到,但是,这个方程在使用前需要满足很多情况,具体的细节就看代码了(全部都进行了空间优化,其中need数组表示前 i 种coin凑出币值 j 需要的最小币数时,需要的coin[i]的数量),因为之前看过《挑战》中多重背包的实现,所以这个稍微变化一下也不是很难了。

      然后,到了收银员找币时的情况,因为题目中说了她会提供无限的零钱,所以此时就是完全背包了,注意好计算顺序即可:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 typedef long long LL;
     6 const int maxn= 10000;
     7 
     8 int dp[maxn+3], coin[103],num[103], need[maxn+3];
     9 
    10 int main(){
    11     int n,t,i,j;
    12     while(~scanf("%d%d",&n,&t)){
    13         for(i=1; i<=n; ++i)
    14             scanf("%d",coin+i);
    15         for(i=1; i<=n; ++i)
    16             scanf("%d",num+i);
    17         memset(dp,-1,sizeof(dp));
    18         dp[0]= 0;
    19         for(i=1; i<=n; ++i){
    20             memset(need,0,sizeof(need));
    21             for(j=0; j<=maxn; ++j)
    22                 if(j>=coin[i]&& dp[j-coin[i]]!= -1 && need[j-coin[i]]<num[i]){
    23                     if(dp[j]== -1 || dp[j]> dp[j-coin[i]]+1){
    24                         dp[j]= dp[j-coin[i]]+1;
    25                         need[j]= need[j-coin[i]]+1;
    26                     }
    27                 }
    28         }
    29         for(i=1; i<=n; ++i)
    30             for(j=maxn-coin[i]; j>=0; --j)
    31                 if(dp[j+coin[i]]!= -1){
    32                     if(dp[j]== -1)    dp[j]= dp[j+coin[i]]+1;
    33                     else    dp[j]= min(dp[j],dp[j+coin[i]]+1);
    34                 }
    35         printf("%d
    ",dp[t]);
    36     }
    37     return 0;
    38 }
    View Code

      因为一开始没用到need数组所以调试了一会儿,然后今早起来想了想发现昨晚写的need数组有问题,不知是数据弱还是怎么的也能过,改了下后提交发现:

      竟然排到了16名,第一次这么前。

      后来又想了想,dp数组如果用INF而不是-1来作为凑不出币值 j 的标记的话,代码会更简单一些:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 typedef long long LL;
     6 const int INF= 0x3fffffff;
     7 const int maxn= 10000;
     8 
     9 int dp[maxn+3], coin[103],num[103], need[maxn+3];
    10 
    11 int main(){
    12     int n,t,i,j;
    13     while(~scanf("%d%d",&n,&t)){
    14         for(i=1; i<=n; ++i)
    15             scanf("%d",coin+i);
    16         for(i=1; i<=n; ++i)
    17             scanf("%d",num+i);
    18         for(j=1; j<=maxn; ++j)
    19             dp[j]= INF;
    20         dp[0]= 0;
    21         for(i=1; i<=n; ++i){
    22             memset(need,0,sizeof(need));
    23             for(j=0; j<=maxn; ++j)
    24                 if(j>=coin[i] && need[j-coin[i]]<num[i]){
    25                     if(dp[j]> dp[j-coin[i]]+1){
    26                         dp[j]= dp[j-coin[i]]+1;
    27                         need[j]= need[j-coin[i]]+1;
    28                     }
    29                 }
    30         }
    31         for(i=1; i<=n; ++i)
    32             for(j=maxn-coin[i]; j>=0; --j)
    33                 dp[j]= min(dp[j],dp[j+coin[i]]+1);
    34         printf("%d
    ",dp[t]==INF? -1:dp[t]);
    35     }
    36     return 0;
    37 }
    View Code

      虽然比起上一个慢了点可以忽略不计的时间:

      anyway,人生第一道AC掉的混合背包,值得纪念,背包问题,继续进取中~~

  • 相关阅读:
    与众不同 windows phone (50)
    与众不同 windows phone (49)
    重新想象 Windows 8.1 Store Apps (93)
    重新想象 Windows 8.1 Store Apps 系列文章索引
    重新想象 Windows 8.1 Store Apps (92)
    重新想象 Windows 8.1 Store Apps (91)
    重新想象 Windows 8.1 Store Apps (90)
    重新想象 Windows 8.1 Store Apps (89)
    重新想象 Windows 8.1 Store Apps (88)
    重新想象 Windows 8.1 Store Apps (87)
  • 原文地址:https://www.cnblogs.com/Newdawn/p/4269014.html
Copyright © 2011-2022 走看看