浅谈动态规划(背包问题)

  1. 01背包
    一个空间大小为M的背包有N个物体可以装进去,每个物体都有所占空间体积和相对应的价值
    怎么样装使得其装的价值最大
    01背包是指再装之前每个物体的价值和重量均唯一不会因为后面的状态改变而发生改变,可认作时按照优先度从1~n排序,虽然再常规的01背包中没有什么用但是一旦遇到价值或者重量随状态改变而发生改变的情况就可以按照其有限度排序即可使用01背包,01背包时一个物体或者一个动作对整体产生变化的做法
    其每个物体都会占体积也有对应的价值,那么就分为一个物体装或者不装,假如用f[i][j]来代表有i个物体背包空间一共为j价值最大的情况,w[i]表示重量,v[i]代表价值,那么就有f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]) ,原理是根据其是否装第i个物体, 其中f[i-1][j]代表上一次装i-1个物体在j的空间下的最大价值情况即代表不装这个物体,同理f[i-1][j-w[i]]+v[i]代表上一次装i-1个物体在j-w[i]的空间下的最大价值情况因为要给这个物体腾出空间所以是j-w[i]最后再加上v[i],该情况为装该物体,
    所以再二维数组的情况下的代码如下:
	for (int i = 1; i <= N; i++)
	{
		for (int j = M; j>=w[i]; j--)
		{
			f[i][j] = max(f[i - 1][j], f[i - 1][j - w[i]] + v[i]);
		}
	}

该情况可简化为一维数组的情况以后的完全背包问题等需要用到一维数组的处理情况最好提前了解:
由于是f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]),可以看出f[i][j]都是基于f[i-1]来做出判断所以可以下称一下形式:

	for (int i = 1; i <= N; i++)
	{
		for (int j = M; j >=w[i]; j--)
		{
			f[j] = max(f[j], f[j - w[i]] + v[i]);
		}
	}

该情况即一直更新一维数组f[j],和上面相比的缺点是无法保存所有的情况,但是可以保留最后一轮的值,

2.完全背包问题
完全背包问题和01背包问题极为相似,不同点在于01背包每种物体只有一个,而完全背包问题的物体有无限个,一样的是求怎么装能够装最大的价值

假如物体的占空间为w[i],价值为v[i],用f[i][j]代表一共i种j的背包容量可以取得的最大价值,由于每个物体的数量无限但是不能超过背包的容量,所以每个物体的最大数量为M/w[i] ,由于是背包问题而01背包问题是所有的背包问题的基础所以用01背包的思想来解决该问题:f[i][j]=max(f[i-1][j],f[i][j-kw[i]]+kv[i]) 其中0<=k*w[i]<=M
解决方法1.将占空间为w[i]价值为v[i]的物品分为W/w[i]件价值为v[i]的物品
解决代码:

int  w[i], v[i];
vector<int> W;
vector<int>V;
int M, N;
int f[500000];
int main()
{
	for (int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= M / w[i]; j++)
		{
			W.push_back(w[i]);
			V.push_back(v[i]);
		}
	}

	N = W.size();
	for (int i = 1; i <= N; i++)
	{
		for (int j = M; j >= V[i]; j--)
		{
			f[j] = max(f[j], f[j - W[i]] + V[i]);
		}
	}
}

解决方法2.由于其的无限个特性,需要清楚认识到为什么01背包问题的第二个for是倒着循环的,因为01背包问题的物体只有选和不选两种情况,为了避免重复所以从后往前搜索一定是再f[i-1][j]得基础上计算得,这里是无限个数所以可以重复相加所以可将第二个for循环变为从w[i]到费用最大值:
解决代码:

	for (int i = 1; i <= N; i++)
	{ 
		for (int j = w[i]; j <= M; j++)
			f[j] = max(f[j], f[j - w[i]] + v[i]);
	}

3.多重背包,相对于前面两个背包多了一个值n[i]代表第i个数一共有n[i]个,那么这就牵扯到两个问题:该物体总值是否大于背包容量,如果大于那么就对该物体采用完全背包,如果没有大于背包容量那么就采用01背包:

解决代码:

int Compelete()
{
	for (int i = 1; i <= N; i++)
	{
		for (int j = V; j >= 0; j--)
		{
			for (int k = 0; k*n[i]<=j; k++)
			{
				if(j>=k*w[i])
				f[j] = max(f[j], f[j - k * w[i]] + k * v[i]);
			}
		}
	}
	return f[V];
}

4.混合背包: 混合背包为上述三个背包的混合即有得物体只能取一次有的物体能取无限次,有的物体能取有限次
那么根据每个物体得情况对每个物体采用不同得措施:

解决代码:

int Compelete()
{
	for (int i = 1; i <= N; i++)
	{
		if (n[i] == 1)//01背包
		{
			for (int j = V; j >= w[i]; j--)
				f[j] = max(f[j], f[j - w[i]] + v[i]);
		}
		else if (n[i] == 0x3f3f3f3f)//完全背包
		{
			for (int j = w[i]; j <= V; j++)
				f[j] = max(f[j], f[j - w[i]] + v[i]);
		}
		else//多重背包
		{
			for (int j = V; j >= 0; j--)
			{
				for (int k = 0; k <= n[i]; k++)
				{
					if (k*w[i] <= V)
						f[j] = max(f[j], f[j - k * w[i]] + k * v[i]);
				}
			}
		}
	}
	return f[V];
}

5.二维背包:针对上述情况多一个条件即每次装入背包时需要多一个消耗,另一个消耗得容量为U
所以每次装一个物体需要消耗V的容量也需要消耗U的容量 价值为v[i]

解决代码:

int TwoPack()
{
	for (int i = 1; i <= N; i++)
	{
		if (n[i] == 1)//01
		{

			for (int j = V, ; j >= w1[i]; j--)
			{
			 for(int l=U;l>=w2[i];l--)
				f[j][l] = max(f[j][l], f[j - w1[i]][l - w2[i]] + v[i]);
			}
		}

		else if (n[i] == 0x3f3f3f3f)//完全背包
		{
			for (int j = w1[i], l = w2[i]; j<=V && l <=U; j++, l++)
			{
				f[j][l] = max(f[j][l], f[j - w1[i]][l - w2[i]] + v[i]);
			}
		}
		else //多重
		{
			for (int j = V, l = U; j >= w1[i] && l >= w2[i]; j--, l--)
			{
				for (int k = 0; k <= n[i]; k++)
				{
					if (k*w1[i] <= V && k*w2[i] <= U)
						f[j][l] = max(f[j][l], f[j - k * w1[i]][l - k * w2[i]] + k * v[i]);
				}
			}
		}
	}
	return   f[V][U];
}

6.分组背包问题:
分组背包为题即为物体分为多个组,只能从每个组中取得一个物体,k代表组数 t[i]代表每组里的物体个数:
解决代码:

int Group_pack()
{
	for (int i = 1; i <= k; i++)
	{
		for (int j = V; j >= 0; j--)
		{
			for (int l = 1; l <= t[i]; l++)
			{
				f[j] = max(f[j], f[j - w[l]] + v[l]);
			}
		}
	}
	return f[V];
}

这里的V0一定要写在1~t[i]之前,因为每个组的只能选一个
/@-@ 蒟蒻还没看懂下面的背包看懂了会及时更新在上面的大佬神犇勿喷/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值