zoukankan      html  css  js  c++  java
  • 背上我可爱的小背包

    题目内容为《背包九讲》内所提及的问题,全部由经典例题改编而来



    A : 买零食

    Time Limit:1.000 Sec Memory Limit:128 MiB

    Description

    贪吃的大三学长ZHB想要买一箱零食

    已知他有一个箱子容量为\(V\)(正整数,\(0 \leqslant V \leqslant 200000\)),商店有\(n\)件零食(\(0<n\leqslant 30\),每件零食有一个体积(正整数))。

    要求\(n\)件零食中,任取若干件装入箱内,ZHB学长买走零食后剩余空间最少。

    Input

    \(1\)个整数,表示箱子容量

    \(1\)个整数,表示有\(n\)个物品

    接下来\(n\)行,分别表示这\(n\)个物品的各自体积

    Output

    \(1\)个整数,表示箱子剩余空间。



    Sample Input

    24
    6
    8
    3
    12
    7
    9
    7
    



    Sample Output

    0
    



    PZ's Solution

    1.经典的\(01\)背包,只是物品的价值与重量相等;

    2.状态转移方程为\(f[j]=max(f[j],f[j-x]+x)\),其中\(x\)为零食的体积;

    2.剩余空间即为\(V-f[V]\)

    • TAG:01背包



    PZ.cpp

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int V,n,x,f[20005];
    int main(){
    	scanf("%d %d",&V,&n);
    	for(int i=1;i<=n;++i){
    		scanf("%d",&x);
    		for(int j=V;j>=x;--j)
    			f[j]=max(f[j],f[j-x]+x);
    	}
    	printf("%d",V-f[V]);
    	return 0;
    }
    






    B : 采药

    Time Limit:1.000 Sec Memory Limit:128 MiB

    Description

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

    Input

    第一行有2个整数\(T(1 \leqslant T \leqslant 1000)\)\(M(1 \leqslant M \leqslant 100)\),用一个空格隔开,\(T\)代表总共能够用来采药的时间,\(M\)代表山洞里的草药的数目。
    接下来的\(M\)行每行包括两个在\(1\)\(100\)之间(包括\(1\)\(100\))的整数,分别表示采摘某株草药的时间和这株草药的价值。

    Output

    \(1\)个整数,表示在规定的时间内可以采到的草药的最大总价值。



    Sample Input

    70 3
    71 100
    69 1
    1 2
    



    Sample Output

    3
    



    More Info

    这是一道非常标准的\(01\)背包问题



    PZ's Solution

    1.\(01\)背包,状态转移方程为\(f[i]=max(f[i],f[i-t]+w)\)

    • TAG:01背包



    PZ.cpp

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int T,M,t,w,f[1005];
    int main(){
    	scanf("%d %d",&T,&M);
    	for(int i=1;i<=M;++i){
    		scanf("%d %d",&t,&w);
    		for(int j=T;j>=t;--j)
    			f[j]=max(f[j],f[j-t]+w);
    	}
    	printf("%d",f[T]);
    	return 0;
    }
    






    C : 燕山采药

    Time Limit:1.000 Sec Memory Limit:128 MiB

    Description

    ZHB已经是一个老年人了,他经常需要一些中药补补身体,但无奈于空空的钱包,他只能自己到燕山上去采药。碰巧的是,他找到了一个山洞,山洞里全部都是草药。ZHB很想把它们全部采走,但受制于自己身体,他只能在有限的时间内去采。已知每一株药草都有对应的药效和采摘时间,请你设计一个算法帮助ZHB,让TA在有限的时间内采摘的药草药效总和最大。

    Input

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

    Output

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



    Sample Input

    70 3
    71 100
    69 1
    1 2
    



    Sample Output

    140
    



    More Info

    本题基于采药改编,有些许不同,请注意



    PZ's Solution

    1.题面表达其实有些问题,但从样例应该能看出来,这是一个完全背包;

    2.完全背包的状态转移方程与\(01\)背包相同,只是循环顺序的问题;

    3.有状态转移方程\(f[i]=max(f[i],f[i-t]+w)\)

    ps.不知道为啥C++的程序挂了,蜜汁问题~

    • TAG:完全背包



    PZ.c

    #include<stdio.h>
    int T,M,t,w,f[100005];
    int main(){
    	scanf("%d %d",&T,&M);
    	for(int i=1;i<=M;++i){
    		scanf("%d %d",&t,&w);
    		for(int j=t;j<=T;++j)
    			if(f[j-t]+w>f[j]) f[j]=f[j-t]+w;
    	}
    	printf("%d",f[T]);
    	return 0;
    }
    






    D : 卖书

    Time Limit:1.000 Sec Memory Limit:128 MiB

    Description

    “卖书了,卖书了!亏本大甩卖啊!”

    大书商小明非常想把手上的\(M\)本书卖给旗下\(N\)家加盟店。已知这些店收购这些书会给小明带来一定的利润,请你设计一个算法,帮助大书商小明获得最大盈利。

    已知\(1\leqslant M\leqslant 15,1\leqslant N \leqslant 10\),小明可以任意数量的书卖给任意加盟店,但总数不超过小明持有的数\(M\)

    Input

    第一行有两个数,第一个数是小明持有的书本数\(N\),第二个数是加盟店店数\(M\)

    接下来是一个\(N*M\)的矩阵,表明了第\(I\)个加盟店卖得\(J\)台书本的盈利。

    Output

    \(1\)行为最大盈利值

    \(2\)到第\(n\)行为第\(x\)个加盟店分\(y\)本所产生的利润

    并使输出的答案字典序最小



    Sample Input

    3 3
    50 60 30
    30 20 80
    60 80 50
    



    Sample Output

    140
    1 1
    2 1
    3 1
    



    More Info

    背包问题经典解法



    Solution

    题解借鉴自sycstudio的CJOJ 1131 机器分配 / Luogu 2066 机器分配 (动态规划)

    1.背包问题,设\(f[i][j]\)代表前\(i\)个加盟店,总共卖出\(j\)本书获得的最大利润;

    2.有状态转移方程\(f[i][j]=max(f[i][j],f[i-1][j-k]+a[i][k])\),其中\(a[i][k]\)代表第\(i\)个加盟店卖出\(k\)本书的利润;

    3.之后难点在于输出字典序最小的方案(题面意思是要输出符合方案的\(x\)\(y\));

    4.这里借鉴了其他博客的做法,使用了一个递归函数,倒退求出了字典序最小的解;

    5.细节见代码;

    • TAG:动态规划;背包



    std.cpp

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int n,m,a[20][20],f[20][20];
    void printans(int N,int M){
    	//N代表第N个加盟店,M代表还有M本书
    	if(N==0) return;
    	for(int i=0;i<=M;++i) //i代表这个加盟店拿走一部份书后,剩余的书本数
    						  //这样,就能保证下一个加盟店拿的更少,保证了字典序方案
    		if(f[N][M]==f[N-1][i]+a[N][M-i]){//如果当前的i可以满足最优的条件,说明这种方案符合答案
    			printans(N-1,i);
    			printf("%d %d\n",N,M-i);
    			break;
    		}
    }
    int main(){
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			scanf("%d",&a[i][j]);
    	for(int i=1;i<=n;++i)
    		for(int j=0;j<=m;++j) //因为有的公司可以一本书不要,所有循环从0开始
    			for(int k=0;k<=j;++k)
    				f[i][j]=max(f[i][j],f[i-1][k]+a[i][j-k]);
    	printf("%d\n",f[n][m]);
    	printans(n,m);//打印方案(注意从后往前递归)
    	return 0;
    }
    






    E : 买零食(二)

    Time Limit:1.000 Sec Memory Limit:128 MiB

    Description

    今天阳光正好,WWN学姐想去超市买几包零食送给正在敲代码的小萌新。

    超市里的商品琳琅满目,大大小小价格不一的商品让热爱思考的学姐想到了一个有趣的问题:

    他手上的袋子可以装下体积为\(V\)的零食,而每件商品都有自己所对应的价格和体积,那么她这次购物最大花销会是多少呢?

    但是聪明的学姐很快就想出了答案,这让她感觉很没有挑战性...于是学姐又继续思考:

    那么我这次购物第\(K\)大花销会是多少呢?

    这次可让学姐把自己给难住了

    那么就请你设计一个算法帮助学姐解决这个问题吧

    Input

    第一行包含一个整数\(T\),代表测试数据组数

    对接下来的\(T\)组数据,每组数据有三行

    第一行有三个整数\(N,V,K(N \leqslant 100 , V \leqslant 1000 , K \leqslant 30)\),分别代表零食数量、学姐手上袋子的体积和所要求的第\(K\)大花销

    第二行有\(N\)个整数,分别代表每件零食的价格

    第三行有\(N\)个整数,分别代表每件零食的体积

    Output

    \(T\)行整数,分别是每组数据的解(解的大小将小于\(2^{31}\)



    Sample Input

    3
    5 10 2
    1 2 3 4 5
    5 4 3 2 1
    5 10 12
    1 2 3 4 5
    5 4 3 2 1
    5 10 16
    1 2 3 4 5
    5 4 3 2 1
    



    Sample Output

    12
    2
    0
    



    More Info

    可以参考背包九讲里面的思路,改自HDUOJ2639



    Solution

    思路借鉴了神回的HDU - 2639 Bone Collector II

    1.如果只求最大,此题即为\(01\)背包的模板;

    2.现在需求第\(K\)大,则设\(f[i][k]\)为体积为\(i\)时的第\(k\)大解;

    3.按照原本的状态转移方程\(f[i]=max(f[i],f[i-v[j]]+w[j])\),只需要求出当前\(i\)体积的所有可能情况,并将这些情况的值进行排序,将前\(k\)大(不重复)的值装入相应的\(f[i][k]\)即可;

    ps.C++又蜜汁挂了,这里直接写了一个快排代替排序;

    • TAG:01背包



    std.c

    #include<stdio.h>
    int T,n,v,k,w[5005],V[5005],F[1005][50],f[5005],cnt,fcnt;
    void quicksort(int l,int r)
    {
        int i=l,j=r,mid=f[(i+j)/2];
        while(i<j)
        {
            while(f[j]<mid) j--;
            while(f[i]>mid) i++;
            if(i<=j)
            {
                int t=f[i];
                f[i]=f[j];
                f[j]=t;
                i++;
                j--;
            }
        }
        if(l<j) quicksort(l,j);
        if(i<r) quicksort(i,r);
    }
    int main(){
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d %d %d",&n,&v,&k);
    		for(int i=1;i<=n;++i)
    			scanf("%d",&w[i]);
    		for(int i=1;i<=n;++i)
    			scanf("%d",&V[i]);
    		for(int i=0;i<=v;++i)
    			for(int j=0;j<=k;++j)
    				F[i][j]=0;
    		for(int i=1;i<=n;++i){
    			for(int j=v;j>=V[i];--j){
    				fcnt=cnt=0;
    				for(int K=1;K<=k;++K){
    					f[++cnt]=F[j][K];
    					f[++cnt]=F[j-V[i]][K]+w[i];
    				}
                    //求出所有可能的情况
    				quicksort(1,cnt);//从大到小排序
    				for(int K=1;K<=cnt;++K){
    					if(fcnt==k) break;
    					if(f[K]==f[K+1]) continue;
    					F[j][++fcnt]=f[K];
    				}
                   	//将不重复的前k大的值装入相应的F[j][K]即可
    			}
    		}
    		printf("%d\n",F[v][k]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    HTML DOM教程 14HTML DOM Document 对象
    HTML DOM教程 19HTML DOM Button 对象
    HTML DOM教程 22HTML DOM Form 对象
    HTML DOM教程 16HTML DOM Area 对象
    ubuntu 11.04 问题 小结
    VC6.0的 错误解决办法 小结
    boot.img的解包与打包
    shell里 截取字符串
    从零 使用vc
    Imagemagick 对图片 大小 和 格式的 调整
  • 原文地址:https://www.cnblogs.com/Potrem/p/NCST_beibao.html
Copyright © 2011-2022 走看看