完全||多重背包

完全背包和01背包的区别在于完全背包里的每一件物品的数量有无数个。
模板

for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= v; ++j) {
        if (c[i] <= j)
            f[i][j] = max(f[i - 1][j],f[i][j - c[i]] + v[i]);
        else
            f[i][j] = f[i - 1][j];
    }
}

与01背包类似的模板

for (int i = 1; i <= n; ++i) {
    for (int j = w[i]; j <= v; ++j) {
        f[j] = max(f[j], f[j - c[i]] + v[i]);
    }
}

 
 
模板题

极度怀疑这个题目有错看,结果应该是10

//#include <bits/stdc++.h>
int dp[101][101],w[101],v[101];
 //方法一
int main(){
    freopen("data","r",stdin);
    int n,W;
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>w[i]>>v[i];
    cin>>W;
    for(int i=0;i<n;i++)
        for(int j=0;j<=W;j++)
            if(j<w[i])
                dp[i+1][j]=dp[i][j];
            else{
                dp[i+1][j]=max(dp[i-1][j],dp[i+1][j-w[i]]+v[i]);
            }
    cout<<dp[n][W];
    return 0;
} 

   //方法二
    for(int i=0;i<n;i++)
        for(int j=w[i];j<=W;j++)
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    cout<<dp[W];

 

一大波例题来啦

Piggy-Bank

#include <bits/stdc++.h>
using namespace std;
long long int dp[1000001],w[10000001],v[1000001];

int main(){
freopen("data","r",stdin);
long long int t,weight,W,n;
scanf("%lld",&t);
while(t--){
    
    //dp[0]=0;
    scanf("%lld%lld",&weight,&W);
    W-=weight;
    //切记,这里不能把dp[0]也设置成inf! 
    for(int i=1;i<=W;i++) dp[i]=1000000000;
    scanf("%lld",&n);
    for(int i=0;i<n;i++)
        scanf("%lld%lld",&v[i],&w[i]);
    for(int i=0;i<n;i++)
        for(int j=w[i];j<=W;j++)
            dp[j]=min(dp[j],dp[j-w[i]]+v[i]);
    
    if(dp[W]==1000000000)
        cout<<"This is impossible."<<endl;
    else
        printf("The minimum amount of money in the piggy-bank is %lld.\n",dp[W]);
}
return 0;
} 

Robberies

这道题很有意思:题目中给定价值和被抓几率,但是被抓几率不可以用乘积来组合计算,举个例子,比如第一个银行3%被抓几率,第二个5%被抓几率,那么乘起来会变成0.15%,抢的越多,被抓几率却越小了,显然不对,因此要转换成不被抓几率,上述例子则变为第一家97%不被抓,第二家95%不被抓,乘起来就是92.15%,抢的越多,不被抓的几率越来越小即被抓几率越来越大,这样才是符合常理的。
因为几率都是小于1的,所以相乘一定会减小,而强的银行越多,被抓率不可能减少//如果是这样,大家都去抢银行了~~

//#include <bits/stdc++.h>
using namespace std;
#define  eps 0.000000000001
double w[10100],W,dp[10100];
int a[10100],n;
int t;
//w[i]是被抓几率,然后转成逃跑几率 
int main(){
//      freopen("data","r",stdin);
    cin>>t;
      while(t--){
        int sum=0;
        memset(dp,0,sizeof(dp));
        scanf("%lf%d",&W,&n);
        //把它变成逃跑几率 
        W=1-W;
        dp[0]=1;//你不抢银行你就不用被抓咯 
        for(int i=0;i<n;i++)
        {
            scanf("%d%lf",&a[i],&w[i]);
            w[i]=1-w[i];
            sum+=a[i];
        }
        for(int i=0;i<n;i++)
//钱数从最多开始循环
            for(int j=sum;j>=a[i];j--)
                dp[j]=max(dp[j],dp[j-a[i]]*w[i]);
            
        for(int i=sum;i>=0;i--){
            if(dp[i]>W){
                printf("%d\n",i);
                break; 
            } 
        }
    
    }
 
    return 0;
} 

 
 

多重背包

例题

珍惜现在,感恩生活

拆分那里刚开始是没有搞懂的,然后看了师兄发的文件,又试着将过程输出了一下,才明白拆分的含义,比如说这道题有四袋米,我可以把它拆成三分,一分是一袋米,一份是两袋米,最后一份还是一袋米(通过补充来计算),将他们存入一个新的数组里面,最后再对这个新的数组进行01背包就可以了嘛~

//#include <bits/stdc++.h>
using namespace std;
int max(int x,int y){
    return x>y?x:y;
}
struct E
{
    int w;  //体积
    int v;  //重量
} lis[2001];

int dp[101];

int main()
{
    int T,n,m;
    int p,h,k;
    int i,j;
    int index,c;
    cin>>T;
    while(T--){
    scanf("%d%d",&n,&m);                //n表示容量,m表示种类
    index = 0;  //拆分后物品总数
    for( i=1; i<=m; i++)
    {
        c = 1;
        scanf("%d%d%d",&p,&h,&k);       //p表示价格,h表示重量,k表示大米袋数。
    //这里开始拆分
        while( k-c>0)
        {
            k -= c;
            lis[++index].w = c*p;
            lis[index].v = c*h;
            c *= 2;
        }
        lis[++index].w = p*k;  //补充不足指数的差值
        lis[index].v = h*k;
    }
    for( i=0; i<=n; i++) dp[i]=0;
    for( i=1; i<=index; i++)   //对拆分后的物品进行0-1背包
    {
        for( j=n; j>=lis[i].w; j--)
            dp[j] = max( dp[j],dp[j-lis[i].w]+lis[i].v);
    }
        printf("%d\n",dp[n]);
    }
    return 0;
}

Coins

其实刚开始看到这个问题时,想到的时完全背包,但是对于一个菜鸟来说,不知道怎么打,然后看了一些大佬的代码,才有了头绪---说是双线背包~
这里的dp起到标记的作用,sum[j]是用来记录每j元需要多少个硬币,ans是用来记录次数。

解法一
//#include <bits/stdc++.h>
using namespace std;
int dp[100001],n,m,v[101],w[101],sum[100001];

int main()
{
    while(cin>>n>>m&&m&&n){
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++)
            cin>>v[i];
        for(int i=0;i<n;i++)
            cin>>w[i];
        dp[0]=1;
        int ans=0;
        for(int i=0;i<n;i++){
            memset(sum,0,sizeof(sum));
            for(int j=v[i];j<=m;j++){
                //这里进行筛选 
                //sum[j]表示当前物品填充j大小的包需要至少使用多少个.
                if(!dp[j]&&dp[j-v[i]]&&sum[j-v[i]]<w[i]){
                    dp[j]=1;
                    sum[j]=sum[j-v[i]]+1;
                    ans++;
                }
            }
        } 
        cout<<ans<<endl;    
    }
    return 0;
}
解法二(多重部分和)
#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;
int dp[100001];
int A[100001],C[1001];
int a[4]={0,1,2,5};
int m;
int n,K=15000;

int main()
{
    while(cin>>n>>m&&(n!=0||m!=0)){
        memset(dp,-1,sizeof(dp));
        dp[0]=0;
        for(int i=0;i<n;i++)
            cin>>A[i];
        for(int i=0;i<n;i++)
            cin>>C[i];
        for(int i=0;i<n;i++)
            for(int j=0;j<=m;j++){
                if(dp[j]>=0)
                    dp[j]=C[i];
                else if(A[i]>j||dp[j-A[i]]<=0)
                    dp[j]=-1;
                else
                    dp[j]=dp[j-A[i]]-1;
            }
        int res=0;
        for(int i=1;i<=m;i++){
            if(dp[i]>=0)
                res++;
        }
        cout<<res<<endl;
    }
    return 0;
}

Cash Machine

这道题和第一道例题很像,只不过没有用结构体每一步都是按照模板来滴

//#include <bits/stdc++.h>
using namespace std;
int dp[100001],n,m,v[101],w[101],sum[100001];
int max(int x,int y){
    return x>y?x:y;
}
int main()
{
    freopen("data","r",stdin);
    int cash,num,denomination;
    while(cin>>cash){
        int index=0;
        memset(v,0,sizeof(v));
        cin>>n;
        for(int i=0;i<n;i++){
            cin>>num>>denomination;//种类和面值
            //接着将种类进行拆分
            int k=1;
            while(num-k>0){
                num-=k;
                v[++index]=k*denomination;
                //w[i]=k*? 因为这里没有类似质量的变量,所以不需要这一步啦
                k*=2; 
            } 
            v[++index]=num*denomination;
        }
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=index;i++)
            for(int j=cash;j>=v[i];j--){//可以试一下不倒过来的 
                dp[j]=max(dp[j],dp[j-v[i]]+v[i]);
            }
        int ans=0;
        for(int i=0;i<=cash;i++)
            ans=max(ans,dp[i]); 
        cout<<ans<<endl;
    }
    return 0;
}

Dividing

原本交了代码却wa了,幸亏没有放弃自己的代码,最后发现是忘记将dp归零了(捂脸

//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
//#include <cstdlib>
#include <cstring>
using namespace std;
int dp[100001],n,m,v[101],w[101],num[100001],nv[101],nw[101];
int max(int x,int y){
    return x>y?x:y;
}
int main()
{
    int m=1;
    //freopen("data","r",stdin);
    while(cin>>w[1]>>w[2]>>w[3]>>w[4]>>w[5]>>w[6]){
        int sum=0,index=0;
        for(int i=1;i<=6;i++){
            int k=1;
            sum+=i*w[i];
            while(w[i]>k){
                nw[++index]=k*i;
                w[i]-=k;
                k*=2;       
            }
            nw[++index]=w[i]*i;
        }
        if(sum==0)
            return 0;
        cout<<"Collection #"<<m++<<":"<<endl;
        if(sum%2){
            cout<<"Can't be divided."<<endl<<endl;
        }else{
            memset(dp,0,sizeof(dp));
            sum/=2;
            for(int i=1;i<=index;i++){
                for(int j=sum;j>=nw[i];j--)
                    dp[j]=max(dp[j],dp[j-nw[i]]+nw[i]);
            }
            if(dp[sum]==sum)
                cout<<"Can be divided."<<endl<<endl;
            else
                cout<<"Can't be divided."<<endl<<endl; 
        }
    }
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容