背包DP

Posted ShadowAA

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了背包DP相关的知识,希望对你有一定的参考价值。

背包DP

二进制分组优化

考虑优化。我们仍考虑把多重背包转化成 0-1 背包模型来求解。

预处理物品数量是2的次方。且要覆盖物品数量的点。即2 n次方+1到k

index = 0;
for (int i = 1; i <= m; i++) 
  int c = 1, p, h, k;
  cin >> p >> h >> k;
  while (k > c) 
    k -= c;
    list[++index].w = c * p;
    list[index].v = c * h;
    c *= 2;
  
  list[++index].w = p * k;
  list[index].v = h * k;

单调队列优化背包

求区间为m中的最小值

t=1;
	for (i=1;i<=n;i++)
	
		if ((b[t]<i-m+1)and(t<=w))
			t++;
		while ((t<=w)and(a[b[w]]>=a[i]))
			w--;
		w++;
		b[w]=i;
		if (i>=m)
			cout<<a[b[t]]<<\' \';
	

单调队列求连续子序列最大和

子序列最大长度为m

for (i=1;i<=n;i++)
	
		cin>>x;
		a[i]=a[i-1]+x;
	
	ma=a[1];
	for (i=1;i<=n;i++)
	
		if ((b[t]<i-m+1)and(t<=w))
			t++;
		ma=max(ma,a[i]-a[b[t]]);
		while ((t<=w)and(a[b[w]]>=a[i]))
			w--;
		w++;
		b[w]=i;
	
	cout<<ma<<\'\\n\';

单调队列优化多重背包

	cin>>n>>m;
	for (i=1;i<=n;i++)
	
		cin>>v>>z>>k;             //价值 重量 数量
		for (j=0;j<=z-1;j++)
		
			t=0;w=0;
			o=(m-j)/z;
			for (l=0;l<=o;l++)
			
				while ((t<w)and(f[j+l*z]-l*v>=c[w-1]))
					w--;
				b[w]=l;
				c[w]=f[j+l*z]-l*v;
				w++;
				while ((t<w)and(b[t]<l-k))
					t++;
				f[j+l*z]=max(f[j+l*z],c[t]+l*v);
			
		
	
	cout<<f[m];

有依赖的背包

金明的预算方案 洛谷1064

#include<bits/stdc++.h>
using namespace std;
int n,m,i,x,q,t,k,j,l,z,vv,pp,f[400005],v[40005],p[40005],a[40005];
vector<int>b[40005];
int main()

	cin>>m>>n;
	for (i=1;i<=n;i++)
	
		cin>>v[i]>>p[i]>>x;
		p[i]=p[i]*v[i];
		if (x==0)
			a[++k]=i;
		else
			b[x].push_back(i);
	
	for (i=1;i<=k;i++)
	
		q=a[i];
		t=b[q].size();
		for (l=m;l>=v[q];l--)
		
			for (j=0;j<=(1<<t)-1;j++)
			
				vv=v[q];pp=p[q];
				for (z=0;z<b[q].size();z++)
					if ((1<<z)&j)
					
						vv=vv+v[b[q][z]];
						pp=pp+p[b[q][z]];
					
				if (l>=vv)
					f[l]=max(f[l],f[l-vv]+pp);
			
		
	
	cout<<f[m]<<\'\\n\';

01背包的第K优解

memset(dp, 0, sizeof(dp));
int i, j, p, x, y, z;
scanf("%d%d%d", &n, &m, &K);
for (i = 0; i < n; i++) scanf("%d", &w[i]);
for (i = 0; i < n; i++) scanf("%d", &c[i]);
for (i = 0; i < n; i++) 
  for (j = m; j >= c[i]; j--) 
    for (p = 1; p <= K; p++) 
      a[p] = dp[j - c[i]][p] + w[i];
      b[p] = dp[j][p];
    
    a[p] = b[p] = -1;
    x = y = z = 1;
    while (z <= K && (a[x] != -1 || b[y] != -1)) 
      if (a[x] > b[y])
        dp[j][z] = a[x++];
      else
        dp[j][z] = b[y++];
      if (dp[j][z] != dp[j][z - 1]) z++;          //策略不同但权值相同的看作重复的就加这句
    
  

printf("%d\\n", dp[m][K]);

多人背包 洛谷1858

#include<bits/stdc++.h>
using namespace std;
int k,n,m,i,j,p,x,y,s,t,w,z,f[5005][55],a[55],b[55];
int main()

	cin>>k>>m>>n;
	for (i=0;i<=m;i++)
		for (j=1;j<=k;j++)
			f[i][j]=-1000000000;
	f[0][1]=0;
	for (i=1;i<=n;i++)
	
		cin>>x>>y;
		for (j=m;j>=x;j--)
		
			for (p=1;p<=k;p++)
			
				a[p]=f[j-x][p]+y;
				b[p]=f[j][p];
			
			t=w=z=1;
			while (((t<=k)or(w<=k))and(z<=k))
			
				if (a[t]>b[w])
					f[j][z]=a[t++];
				else f[j][z]=b[w++];
				z++;
			
		
	
	for (i=1;i<=k;i++)
		s+=f[m][i];
	cout<<s<<\'\\n\';

01背包与完全背包(dp复习)

01背包和完全背包都是dp入门的经典,我的dp学的十分的水,借此更新博客的机会回顾一下

01背包:给定总容量为maxv的背包,有n件物品,第i件物品的的体积为w[i],价值为v[i],问如何选取才能是背包内的物品价值总和最大。

stdin:

5

1 2 3 4 5

5 4 3 2 1

stdout:

14

设dp[i][j]为取前i件物品时容量为j的最优解。

状态转移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);

压缩后:dp[j]=max(dp[j],dp[j-w[i]]+v[i]);

for (int i = 1; i <= 4; i++) {
        for (int j = 1; j <= bagV; j++) {
            if (j < w[i])
                dp[i][j] = dp[i - 1][j];
            else
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
        }
    }//二维dp
for (int i = 1; i <= 4; i++) {
        for (int j = maxv; j >= w[i]; j--) {
                dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
        }
    }//一维

完全背包:在01背包的基础上,每件物品都不限次数。

从一维数组上区别0-1背包和完全背包差别就在循环顺序上,0-1背包必须逆序,因为这样保证了不会重复选择已经选择的物品,而完全背包是顺序,顺序会覆盖以前的状态,所以存在选择多次的情况,也符合完全背包的题意。状态转移方程都为dp[j] = max(dp[j],dp[j-w[i]]+v[i])。

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

 

以上是关于背包DP的主要内容,如果未能解决你的问题,请参考以下文章

背包DP

动态规划专题

背包DP题单★

背包dp(完全)

hdu 2844 混合背包背包dp

DP基础总结