## 动态规划之背包问题

几种经典背包问题:

1.01背包:一个旅行者有一个最多能装 M 公斤的背包,现在有 n 件物品,它们的重量分别是W_1,W_2,…,W_n,它们的价值分别为C_1,C_2,…,C_n,求旅行者能获得最大总价值。

int main() 
{ 
    scanf("%d%d",&m,&n); 
    for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&c[i]); 
    for(int i=1;i<=n;i++) 
        for(int j=m;j>=w[i];j--) 
            dp[j]=max(dp[j],dp[j-w[i]]+c[i]); 
    printf("%d",dp[m]); 
} 

背包要装满的01背包

 f[i]=-1000000;   
 //......
    for(i=1;i<=n;i++)   
    for(j=m;j>=w[i];j--)   
    f[j]=max(f[j],f[j-w[i]]+c[i]);   
    if(f[m]>0) cout<<f[m];   
    else cout<<-1; 

2.完全背包问题:设有n种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为M,今从n种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于M,而价值的和为最大

 for(int i=1;i<=n;i++) 
        for(int      j=w[i];j<=m;j++      ) 
            dp[j]=max(dp[j],dp[j-w[i]]+c[i]); 

3.混合背包:一个旅行者有一个最多能用V公斤的背包,现在有n件物品,它们的重量分别是W1,W2,…,Wn,它们的价值分别为C1,C2,…,Cn.有的物品只可以取一次,有的物品可以取无限次,有的物品可以取得次数有一个上限,求将哪些物品装入背包这些物品的费用总和不超过背包容量且价值总和最大。

第1行:两个整数V(背包容量,V<=200),n(n<=30,物品数量)
第2至第n行:每行3个整数wi,ci,pi,wi为物品重量,ci为价值,pi如果为0,则表明次物品可以取无数次,如果为其他数字,则次物品最多取的件数为pi

int main() 
{ 
    scanf("%d%d",&m,&n); 
    for(int i=1;i<=n;i++) 
        scanf("%d%d%d",&w[i],&c[i],&p[i]); 
    for(int i=1;i<=n;i++) 
    { 
        if(p[i]==0) 
        { 
            for(int j=w[i];j<=m;j++) 
                f[j]=max(f[j],f[j-w[i]]+c[i]); 
        } 
      **  else
        { 
            for(int j=1;j<=p[i];j++) 
            { 
                for(int k=m;k>=w[i];k--) 
                    f[k]=max(f[k],f[k-w[i]]+c[i]); 
            } 
        } 
    } 
    **
    printf("%d",f[m]); 
}

4.(较难)分组背包:一个旅行者有一个最多能用V公斤的背包,现在有n件物品,它们的重量分别是W1,W2,…,Wn,它们的价值分别为C1,C2,…,Cn.这些物品被划分为若干组,每组中的物品相互冲突,最多选一件。求解将哪些物品装入背包可使被装入物品的容量不超过V,且价值总和最大。

第1行:三个整数V(背包容量,V<=200),n(n<=30,物品数量)和T(最大组号,T<=10)
第2至第n行:每行3个整数wi,ci,pi,wi为物品重量,ci为价值,pi为组号
输出
一行,一个数,表示最大总价值
样例输入
10 6 3
2 1 1
3 3 1
4 8 2
6 9 2
2 8 3
3 9 3

int main() 
{ 
    scanf("%d%d%d",&v,&n,&t); 
    for(int i=1;i<=n;i++) 
    { 
        scanf("%d%d%d",&w[i],&c[i],&p); 
       ** a[p][++a[p][0]]=i; 
    } 
    for(int k=1;k<=t;k++) 
        for(int j=v;j>=0;j--) 
            for(int i=1;i<=a[k][0];i++) 
                if(j>=w[a[k][i]]) 
                    {    
                        int m=a[k][i]; 
                        if(f[j]<f[j-w[m]]+c[m]) 
                        f[j]=f[j-w[m]]+c[m]; 
                    } 
    printf("%d",f[v]); 
    return 0; 
}

课后练习(精华):

问题 A: 【一本通基础DP背包】宠物小精灵之收服

时间限制: 1 Sec 内存限制: 64 MB
提交: 16 解决: 11
[提交][状态]
题目描述
宠物小精灵是一部讲述小智和他的搭档皮卡丘一起冒险的故事。

一天,小智和皮卡丘来到了小精灵狩猎场,里面有很多珍贵的野生宠物小精灵。小智也想收服其中的一些小精灵。然而,野生的小精灵并不那么容易被收服。对于每一个野生小精灵而言,小智可能需要使用很多个精灵球才能收服它,而在收服过程中,野生小精灵也会对皮卡丘造成一定的伤害(从而减少皮卡丘的体力)。当皮卡丘的体力小于等于0时,小智就必须结束狩猎(因为他需要给皮卡丘疗伤),而使得皮卡丘体力小于等于0的野生小精灵也不会被小智收服。当小智的精灵球用完时,狩猎也宣告结束。

我们假设小智遇到野生小精灵时有两个选择:收服它,或者离开它。如果小智选择了收服,那么一定会扔出能够收服该小精灵的精灵球,而皮卡丘也一定会受到相应的伤害;如果选择离开它,那么小智不会损失精灵球,皮卡丘也不会损失体力。

小智的目标有两个:主要目标是收服尽可能多的野生小精灵;如果可以收服的小精灵数量一样,小智希望皮卡丘受到的伤害越小(剩余体力越大),因为他们还要继续冒险。

现在已知小智的精灵球数量和皮卡丘的初始体力,已知每一个小精灵需要的用于收服的精灵球数目和它在被收服过程中会对皮卡丘造成的伤害数目。请问,小智该如何选择收服哪些小精灵以达到他的目标呢?

输入
输入数据的第一行包含三个整数:N(0<N<1000),M(0<M<500),K(0<K<100),分别代表小智的精灵球数量、皮卡丘初始的体力值、野生小精灵的数量。

之后的K行,每一行代表一个野生小精灵,包括两个整数:收服该小精灵需要的精灵球的数量,以及收服过程中对皮卡丘造成的伤害。

输出
输出为一行,包含两个整数:C,R,分别表示最多收服C个小精灵,以及收服C个小精灵时皮卡丘的剩余体力值最多为R。

样例输入
10 100 5
7 10
2 40
2 50
1 20
4 20
样例输出
3 30

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int l,m,n,minn;
int A[105][2];
int f[1005][505];
int main()
{
	scanf("%d %d %d",&m,&l,&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d %d",&A[i][0],&A[i][1]);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=m;j>=A[i][0];j--)
		{
			for(int k=l;k>=A[i][1];k--)
			{
				f[j][k]=max(f[j][k],f[j-A[i][0]][k-A[i][1]]+1);
			}
		}
	}
	printf("%d ",f[m][l]);
	for(int i=0;i<=l;i++)
	{
		if(f[m][i]==f[m][l])
		{
			minn=i;
			break;
		}
	}
	printf("%d",l-minn);

} 

问题 B: 【一本通基础DP背包】开餐馆

时间限制: 1 Sec 内存限制: 64 MB
提交: 17 解决: 13
[提交][状态]
题目描述
信息学院的同学小明毕业之后打算创业开餐馆.现在共有 n n 个地点可供选择。小明打算从中选择合适的位置开设一些餐馆。这 n n 个地点排列在同一条直线上。我们用一个整数序列 m 1 , m 2 , . . . m n m_1,m_2,...m_n 来表示他们的相对位置。由于地段关系,开餐馆的利润会有所不同。我们用 p i p_i 表示在 m i m_i 处开餐馆的利润。为了避免自己的餐馆的内部竞争,餐馆之间的距离必须大于 k k 。请你帮助小明选择一个总利润最大的方案。

输入
输入第一行是整数 T ( 1 T 1000 ) T(1≤T≤1000) ,表明有T组测试数据。紧接着有 T T 组连续的测试。每组测试数据有 3 3 行。

第1行:地点总数 n ( n &lt; 100 ) n(n&lt;100) , 距离限制 k ( k &gt; 0 k &lt; 1000 ) k(k&gt;0 且 k&lt;1000)

第2行:n 个地点的位置 m 1 , m 2 , . . . m n ( 1000000 &gt; m i &gt; 0 m_1 , m_2, ... m_n(1000000&gt;m_i&gt;0 且为整数,升序排列);

第3行:n 个地点的餐馆利润 p 1 , p 2 , . . . p n ( 1000 &gt; p i &gt; 0 p_1,p_2,...p_n(1000&gt;p_i&gt;0 且为整数)。

输出
对于每组测试数据可能的最大利润。

样例输入
2
3 11
1 2 15
10 2 30
3 16
1 2 15
10 2 30
样例输出
40
30


#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
#define LL long long
 
LL max(LL x,LL y)
{
	return x>y?x:y;
 } 
int main()
{
	int t,n,k;
	long long a[110],b[110],c[110];
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&k);
		for(int i=0;i<n;i++)
			scanf("%lld",&a[i]);
		for(int i=0;i<n;i++)
		{
			scanf("%lld",&b[i]);
			c[i]=b[i];	//记录每个位置的初始总利润 
		}		
		for(int i=1;i<n;i++)
		{
			for(int j=0;j<i;j++)
			{
				if(a[i]-a[j]>k)
				{
					c[i]=max(c[i],c[j]+b[i]);//每个位置的最大利润 
				}
				else
					continue;
			}
		}
		long long maxn=0;
		for(int i=0;i<n;i++)
		{
			maxn=max(maxn,c[i]);
		}
		printf("%lld\n",maxn);
	}
	return 0;
 }

猜你喜欢

转载自blog.csdn.net/weixin_43400023/article/details/83317583