• 背上我可爱的小背包


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



    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;
    }
    
  • 相关阅读:
    浅谈ASP.NET内部机制(四)
    用正则表达式看.NET编程正则核心对象详解(三 )
    【讨论】对技术的掌握到底应该又多深?
    掌握XML系列(三)创建格式良好的饿XML文档 续编
    浅谈ASP.NET的内部机制(一)
    浅谈ASP.NET内部机制(三)
    浅谈ASP.NET的内部机制(二)
    小工具大智慧
    老生常谈:抽象工厂模式
    又说:程序员的成长过程
  • 原文地址:https://www.cnblogs.com/Potrem/p/NCST_beibao.html
Copyright © 2020-2023  润新知