zoukankan      html  css  js  c++  java
  • 20200404(背包)

    1、搭配购买

    一道并查集加上01背包的题目,通过并查集的方式将属于一个集合的物品封装成一个物品,然后就跟01背包做法相似。

    【问题描述】
    Joe 觉得云朵很美,决定去山上的商店买一些云朵。商店里有 n 朵云,云朵被编
    号为 12,…...,n,并且每朵云都有一个价值。但是商店老板跟他说,一些云
    朵要搭配来买才好,所以买一朵云则与这朵云有搭配的云都要买。
    但是 Joe 的钱有限,所以他希望买的价值越多越好。
    【输入说明】
    第 1 行 n,m,w,表示 n 朵云,m 个搭配,Joe 有 w 的钱。
    第 2~n+1 行,每行 ci,di 表示 i 朵云的价钱和价值。
    第 n+2~n+1+m 行,每行 ui,vi,表示买 ui 就必须买 vi,同理,如果买 vi 就必须
    买 ui。【输出说明】
    一行,表示可以获得的最大价值。
    【输入样例】
    5 3 10
    3 10
    3 10
    3 10
    5 100
    10 1
    1 3
    3 2
    4 2
    【输出样例】
    1
    【数据范围】
    30%的数据保证:n<=100
    50%的数据保证:n<=1,000;m<=100;w<=1,000
    100%的数据保证:n<=10,0000<=m<=5000;w<=10,000
    搭配购买
    #include <bits/stdc++.h>
    using namespace std;
    const int maxx=1e4+5;
    int c[maxx],d[maxx],f[maxx];
    int fa[10005],pri[10005],val[10005];
    int find(int x)
    {
        return (fa[x]!=x)?fa[x]=find(fa[x]):x;
    }
    int main()
    {
        int n,m,w,u,v;
        scanf("%d %d %d",&n,&m,&w);
        for(int i=1;i<=n;i++)
        {
            scanf("%d %d",&c[i],&d[i]);
            fa[i]=i;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d",&u,&v);
            int x=find(u);
            int y=find(v);
            if(x!=y) fa[x]=y;
        }
        for(int i=1;i<=n;i++)
        {
            int x=find(i);
            pri[x]+=c[i];//同一个集合里面的总费用
            val[x]+=d[i];//总价值 
        }
        int count=0;
        for(int i=1;i<=n;i++)
        {
            if(fa[i]==i)
            {
                pri[++count]=pri[i];
                val[count]=val[i];
            }
        }//封装成一个物品
        for(int i=1;i<=count;i++)
            for(int j=w;j>=pri[i];j--)
                f[j]=max(f[j],f[j-pri[i]]+val[i]);//01背包
        printf("%d",f[w]);
        return 0;
    }
    View Code

    2、Dima and Salad

    n个物品,k为倍数。每个物品有两个属性(ai和bi),求在满足所取物品的a属性和是b属性和的k倍的前提下,问a属性的最大值是多少

    按照题意,就是要让我们选出一些组aibi,使得:

    (a1+a2+...+aj)/(b1+b2+...+bj=k

    然后移项,得a1+a2+...+aj=k(b1+b2+...+bj)

    (a1b1k)+(a2b2k)+...+(ajbjk)=0(a1−b1k)+(a2−b2k)+...+(aj−bjk)=0

    观察公式,发现这其实是一个容量为0的01背包,

    我们可以把aibik看作一个物品的体积,ai看作价值,然后一个标准的01背包模板

    那么,背包的容量?容量为0怎么枚举呢?

    先来想一想,(aibik)是不是有可能为负数?那么怎么办呢?

    可以考虑开两个背包,容量分别为V和-V,那么加起来就抵消为0,正容量的背包处理正体积的,负容量的背包处理负体积的

    【问题描述】
    在冰箱里有 n 个水果,每种水果都有两个参数:味道和热量。现在要从冰箱里拿
    些水果出来做一份水果沙拉。选择水果时遵循一定的原则:所选水果的总味道与
    总卡路里的比率必须等于 k。
    请计算在这种原则下,所选水果的最大味道值是什么?
    【输入说明】
    输入的第一行包含两个整数 n,k(1 ≤ n ≤ 100, 1 ≤ k ≤ 10)。
    输入的第二行包含 n 个整数 a1, a2, …, an(1 ≤ ai ≤ 100),表
    示每种水果的味道值。
    输入的第三行包含 n 个整数 b1, b2, …, bn(1 ≤ bi ≤ 100),表
    示每种水果的卡路里,ai 和 bi 是一一对应的。
    【输出说明】
    如果无法选择沙拉所需的水果,请在第 1 行输出-1。
    否则输出一个整数,即所选水果味道值的最大可能总和。
    【输入样例 13 2
    10 8 1
    2 7 1
    【输出样例 118
    【输入样例 25 3
    4 4 4 4 4
    2 2 2 2 2
    【输出样例 2-1
    Dima and Salad
    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e6+5;
    const int inf = 1e8;
    const int maxx=10000;
    int f[N],g[N]; 
    int a[110],b[110];
    int n,k; 
    int main()
    {
        int num; 
        scanf("%d%d",&n,&k); 
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++) scanf("%d",&b[i]); 
        for(int i=1;i<=maxx;i++) f[i]=g[i]=-inf ; 
        for ( int i=1;i<=n;i++ ) {
            num=a[i]-b[i]*k; 
            if(num>=0) {
                for(int j=maxx;j>=num;j--) f[j]=max(f[j],f[j-num]+a[i]);//为正数的时候 
            }
            else{
                for(int j=maxx;j>=-num;j--) g[j]=max(g[j],g[j+num]+a[i]);//为负数的时候 
            }
        }
        int ans=-1; 
        for(int i=maxx;i>=0;i-- ) 
            ans=max(ans,f[i]+g[i]); 
        if(ans<=0) ans=-1 ;
        cout<<ans<<endl; 
        return 0; 
    }
    View Code

    3、最佳策略

    其实就是一个分组背包

    【问题描述】
    经历了无数次编程竞赛失败以后,小明明白了一个道理:做一题就要对一题!但
    是要完全正确地做对一题是要花很多时间(包括调试时间),而竞赛的时间有限。
    所以开始做题之前最好先认真审题,估算一下每一题如果要完全正确地做出来所
    需要的时间,然后选择有把握的题目先做。如果做完了预先选择的题目之后还
    有时间,但是这些时间又不足以完全解决一道题目,应该把其他的题目用贪心之
    类的算法随便做做,争取“骗”一点分数。
    【输入说明】
    第 1 行,两个正整数 N 和 T,表示题目的总数以及竞赛的时限(秒)
    以下的 N 行,每行四个正整数 W1、T1、W2、T2,分别表示第 i 题完全正确做
    出来的得分,完全正确做出来所花费的时间(秒),“骗”来的分数,“骗”分所
    花费的时间(秒)。
    【输出说明】
    根据每一题解题时间的估计值,确定一种做题方案(即哪些题目认真做,哪些题
    目“骗”分,哪些不做),使能在限定的时间内获得最高的得分。
    【数据范围】
    3≤N≤302≤T≤1.08×10^61≤W1i、W2i≤3×10^41≤Tli、T2i≤T
    【输入样例 14 10800
    18 3600 3 1800
    22 4000 12 3000
    28 6000 0 3000
    32 8000 24 6000
    【输出样例 150
    【输入样例 23 7200
    50 5400 10 900
    50 7200 10 900
    50 5400 10 900
    【输出样例 270
    最佳策略
    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e6+5;
    long long f[N],s[35][4],w[35][4];
    int n,m;
    int main(){
        cin>>n>>m;
        for(int i=1;i<=n;i++)
            scanf("%d %d %d %d",&s[i][1],&w[i][1],&s[i][2],&w[i][2]);
        for(int i=1;i<=n;i++)
            for(int j=m;j>=0;j--)
                  for(int k=1;k<=2;k++){
                      if(j>=w[i][k]){
                        f[j]=max(f[j],f[j-w[i][k]]+s[i][k]);
              }
          }
        cout<<f[m]<<endl;
        return 0;
    }
    View Code

    4、升级游戏

    就是一个多组数据的二维费用背包,状态表示为f[i][j]:kill i个怪 消耗的厌恶值不超过j的最大经验值

    【问题描述】
    最近有一款叫做 FATE 的游戏,Joe 为了得到极品装备,他需要不停的杀怪做任
    务。久而久之开始对杀怪产生厌恶感,但又不得不通过杀怪来升完这最后一级。
    现在的问题是,升掉最后一级还需 n 的经验值,他还留有 m 的忍耐度,每杀一
    个怪会得到相应的经验,并减掉相应的忍耐度。当忍耐度降到 0 或者 0 以下时,
    他就不会玩这游戏。Joe 还说了他最多只杀 s 只怪。请问他能升掉这最后一级吗?
    【输入说明】
    输入数据有多组,对于每组数据第一行输入 n,m,k,s(0 < n,m,k,s < 100)四个正
    整数。分别表示还需的经验值,保留的忍耐度,怪的种数和最多的杀怪数。接下
    来输入 k 行数据。每行数据输入两个正整数 a,b(0 < a,b < 20);分别表示杀掉一
    只这种怪会得到的经验值和会减掉的忍耐度。(每种怪都有无数个)
    【输出说明】
    输出升完这级还能保留的最大忍耐度,如果无法升完这级输出-1。
    【输入样例】
    10 10 1 10
    1 1
    10 10 1 9
    1 1
    9 10 2 10
    1 1
    2 2
    【输出样例】
    0
    -1
    1
    升级游戏
    #include <bits/stdc++.h>
    using namespace std;
    const int N=1005;
    int f[N][N],a[N],b[N];
    int n,m,k,s;
    int main()
    {     
        while(cin>>n>>m>>k>>s){
            memset(f,0,sizeof(f));
            memset(a,0,sizeof(a));
            memset(b,0,sizeof(b));
            for(int i=1;i<=k;i++){
                scanf("%d %d",&a[i],&b[i]);
            }
            for(int i=1;i<=k;i++)
                for(int j=1;j<=s;j++)//做多能杀的怪物
                    for(int l=b[i];l<=m;l++)//因为每种怪物无限个,完全背包
                        f[j][l]=max(f[j][l],f[j-1][l-b[i]]+a[i]);
            int ans=-1;
            for(int i=0;i<=m;i++){
                if(f[s][i]>=n)
                {
                    ans=i;
                    break;
                }
            }
            if(ans==-1) printf("-1
    ");
            else printf("%d
    ",m-ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    BFC
    js异常处理
    vue双向数据绑定的简单实现
    cookie封装,localstage封装
    问题 1476: [蓝桥杯][基础练习VIP]龟兔赛跑预测 (模拟)
    HDU 6205 (模拟) card card card
    HDU 4545 (模拟) 魔法串
    HDU 4521 小明系列问题——小明序列 (线段树 单点更新)
    基础动态规划 讲解
    HDU 1561 The more, The Better (有依赖背包 || 树形DP)
  • 原文地址:https://www.cnblogs.com/sunny99/p/12631749.html
Copyright © 2011-2022 走看看