zoukankan      html  css  js  c++  java
  • 2017.10.27

    今天主要复习背包内容了,比较基础的dp,好久不打仍然会手生。其实复习考了这么多次,发现我们其实考试的大部分内容都是学过的,但是经常会因为不常打造成“这个我学过,但写不出来”的尴尬局面。所以经常复习,刷刷版子还是很重要的。

    还有几道水的就不写了,放几道今天稍微要想一下的

    1.金明的预算方案


     

    题目描述

    金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:

    主件 附件

    电脑 打印机,扫描仪

    书柜 图书

    书桌 台灯,文具

    工作椅 无

    如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有0个、1个或2个附件。附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是10元的整数倍)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

    设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,……,jk,则所求的总和为:

    v[j1]*w[j1]+v[j2]*w[j2]+ …+v[jk]*w[jk]。(其中*为乘号)

    请你帮助金明设计一个满足要求的购物单。

    输入输出格式

    输入格式:

    输入的第1行,为两个正整数,用一个空格隔开:

    N m (其中N(<32000)表示总钱数,m(<60)为希望购买物品的个数。)

    从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有3个非负整数

    v p q (其中v表示该物品的价格(v<10000),p表示该物品的重要度(1~5),q表示该物品是主件还是附件。如果q=0,表示该物品为主件,如果q>0,表示该物品为附件,q是所属主件的编号)

    输出格式:

    输出只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<200000)。

    输入输出样例

    输入样例#1: 
    1000 5
    800 2 0
    400 5 1
    300 5 1
    400 3 0
    500 2 0
    
    输出样例#1: 
    2200


    题解:对于每一个主件,把它以及它搭配各附件的情况分别列举,转换成分组背包。
    因为价格是10的倍数,除10后处理,就会优化时空复杂度。
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int n,m,v,p,q,dp[30010],ans,k,mp[30010];
    
    struct node{
        int v,w;
        node(int x,int y):v(x),w(y){}
        node(){}
    };
    vector<node> a[65];
    int main(){
        scanf("%d%d",&n,&m);
        n/=10;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&v,&p,&q);
            v/=10;
            if(q==0){
                a[++k].push_back(node(v,v*p));
                mp[i]=k;
            }
            else{
                int len=a[mp[q]].size();
                for(int j=0;j<len;j++){
                    a[mp[q]].push_back(node(v+a[mp[q]][j].v,a[mp[q]][j].w+v*p)); 
                }
            }
        }
        for(int i=1;i<=k;i++){
            for(int j=n;j>=0;j--)
                for(int k=0;k<(a[i].size());k++){
                    if(j>=a[i][k].v)dp[j]=max(dp[j],dp[j-a[i][k].v]+a[i][k].w);
                }
        }
        printf("%d
    ",dp[n]*10);
        return 0;
    }

    2.疯狂的采药


     

    题目背景

    此题为NOIP2005普及组第三题的疯狂版。

    此题为纪念LiYuxiang而生。

    题目描述

    LiYuxiang是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

    如果你是LiYuxiang,你能完成这个任务吗?

    此题和原题的不同点:

    1.每种草药可以无限制地疯狂采摘。

    2.药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!

    输入输出格式

    输入格式:

    输入第一行有两个整数T(1 <= T <= 100000)和M(1 <= M <= 10000),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到10000之间(包括1和10000)的整数,分别表示采摘某种草药的时间和这种草药的价值。

    输出格式:

    输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

    输入输出样例

    输入样例#1:
    70 3
    71 100
    69 1
    1 2
    
    输出样例#1:
    140
    

    说明

    对于30%的数据,M <= 1000;

    对于全部的数据,M <= 10000,且M*T<10000000(别数了,7个0)。

    题解:完全背包,挺裸的。就是把01背包正过来做。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define maxn 10000010
    using namespace std;
    int t[maxn],v[maxn],f[maxn];
    int main()
    {
        int m,n;
        scanf("%d%d",&m,&n);
        for(int i=1;i<=n;i++)
           scanf("%d%d",&t[i],&v[i]);
        for(int i=1;i<=n;i++)
          for(int j=t[i];j<=m;j++)
            f[j]=max(f[j],f[j-t[i]]+v[i]);
        printf("%d
    ",f[m]);
        return 0;
    }

    3.二叉苹果树


     

    题目描述

    有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)

    这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。

    我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树

    2 5 / 3 4 / 1 现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。

    给定需要保留的树枝数量,求出最多能留住多少苹果。

    输入输出格式

    输入格式:

    第1行2个数,N和Q(1<=Q<= N,1<N<=100)。

    N表示树的结点数,Q表示要保留的树枝数量。接下来N-1行描述树枝的信息。

    每行3个整数,前两个是它连接的结点的编号。第3个数是这根树枝上苹果的数量。

    每根树枝上的苹果不超过30000个。

    输出格式:

    一个数,最多能留住的苹果的数量。

    输入输出样例

    输入样例#1: 
    5 2
    1 3 1
    1 4 10
    2 3 20
    3 5 20
    
    输出样例#1: 
    21

    题解:树形dp,算比较难的一类dp吧,不过这道题还不是很难。
    dp[i][j]表示以i为根保留j条的最优解。要先建树,然后dfs。
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    int dp[2010][2010],w,head[2010],fa[2010],tot,n,tt,sum,cnt,q,u,v;
    struct edge{
        int next,v,w;
    }E[2010];
    void add(int u,int v,int w){
        E[++cnt].v=v;
        E[cnt].w=w;
        E[cnt].next=head[u];
        head[u]=cnt;
    }
    int dfs(int x){
        int ss=0;
        for(int i=head[x];i;i=E[i].next){
            int v=E[i].v,w=E[i].w;
            if(v==fa[x])continue;
            fa[v]=x;
            ss+=dfs(v)+1;
            for(int j=min(q,ss);j>=1;j--)
                for(int k=min(q,j);k>=1;k--)
                    dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[v][k-1]+w);
        }
        return ss;
    }
    int main(){
        scanf("%d%d",&n,&q);
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);add(v,u,w);
        }
        dfs(1);
        printf("%d",dp[1][q]);
        return 0;
    }
  • 相关阅读:
    使用 libevent 和 libev 提高网络应用性能
    An existing connection was forcibly closed by the remote host
    各种浏览器的兼容css
    vs输出窗口,显示build的时间
    sass
    网站设置404错误页
    List of content management systems
    css footer not displaying at the bottom of the page
    强制刷新css
    sp_executesql invalid object name
  • 原文地址:https://www.cnblogs.com/Requiescat/p/7745542.html
Copyright © 2011-2022 走看看