首页 > 其他分享 >背包DP

背包DP

时间:2024-02-17 15:11:27浏览次数:23  
标签:cnt 背包 int cin -- include DP

下面介绍一下背包DP主要题型
基础模型(没什么好说的,上模板)

1.01背包
最主要的模板,应用很多,很重要!!!
倒着遍历容积,不会受后选小的f[i][j]影响,已经选过的物品不会再选一遍。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=10020;
int f[N];
int n,m;
int v[N],w[N],s[N];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>v[i]>>w[i];
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=m;j>=v[i];j--)
		{
			f[j]=max(f[j],f[j-v[i]]+w[i]);
		}
	}
	cout<<f[m];
}

2.完全背包
对于01背包来说,物品可以无限选了。
实现也很简单,只要把01背包的容积倒着遍历改为正着遍历即可。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=250;
int f[N];
int n,m;
int v[N],w[N];
int main()
{
	cin>>m>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>v[i]>>w[i];
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=v[i];j<=m;j++)
		{
			f[j]=max(f[j],f[j-v[i]]+w[i]);
		}
	}
	cout<<"max="<<f[m];
}

3.多重背包
相较于完全背包,物品的个数有限制了。
这是加上二进制优化的最终版本,最后也是将其转化为了01背包。

点击查看代码
#include<iostream>
#include<algorithm> 
#include<cstring> 
#include<cstdio> 
#include<cmath> 
using namespace std;
const int N=8000;
int n,cnt,v[N],f[N],m,C,w[N],s[N];
int main()
{
//	cin>>C;
//	while(C--)
//	{
		cin>>n>>m;
		for(int i=1;i<=n;i++)
		{
			int wi,vi,si;
			cin>>vi>>wi>>si;
			if(si>m/vi)
			{
				si=m/vi;
			}
			for(int j=1;j<=si;j<<=1)
			{
				v[++cnt]=j*vi;
				w[cnt]=j*wi;
				si-=j; 
			}
			if(si>0)
			{
				v[++cnt]=si*vi;
				w[cnt]=si*wi;
			}
		}
		for(int i=1;i<=cnt;i++)
		{
			for(int j=m;j>=v[i];j--)
			{
				f[j]=max(f[j],f[j-v[i]]+w[i]);
			}
		}
		cout<<f[m];
//	}
}

4.二维背包
在容积限制的基础上又加上了质量限制,只需再加一维即可。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=1000;
int f[N][N];
int n,M,cnt,V;
int v[N],w[N],m[N];
int main()
{
	cin>>V>>M;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>v[i]>>m[i]>>w[i];
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=V;j>=v[i];j--)
		{
			for(int k=M;k>=m[i];k--)
			{
				f[j][k]=max(f[j][k],f[j-v[i]][k-m[i]]+w[i]);
			}
		}
	}
	cout<<f[V][M];
}

5.分组背包
把物品分组,每组只能买一个。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=1000;
int f[N];
int n,m,cnt;
int v[N][N],w[N][N],s[N];
int main()
{
	int T,wi,ci,Ti;
	cin>>m>>n>>T;
	for(int i=1;i<=n;i++)
	{
		cin>>wi>>ci>>Ti;
		s[Ti]++;
		w[Ti][s[Ti]]=ci;
		v[Ti][s[Ti]]=wi;
	}
	for(int i=1;i<=T;i++)
	{
		for(int j=m;j>=0;j--)
		{
			for(int k=1;k<=s[i];k++)
			{
				if(j>=v[i][k])
				{
					f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
				}
			}
		}
	}
	cout<<f[m];
}

这些就是基础模板,接下来是例题,你会神奇地发现原来背包还能这么用。

砝码称重

这里我们就可以抽象地把背包看成可称的重量,砝码的重量既是价值,也是容积。
最后遍历一遍有数的背包,就表示可以称出该重量。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=10020;
int f[N],w[10]={0,1,2,3,5,10,20};
int v[N],flag[N],cnt,n,m;
int main()
{
	for(int i=1;i<=6;i++)
	{
		int x;
		cin>>x;
		for(int j=1;j<=x;j++)
		{
			n++;
			v[n]=w[i];
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1000;j>=v[i];j--)
		{
			f[j]=max(f[j],f[j-v[i]]+v[i]);
			flag[f[j]]=1;
		}
	}
	for(int i=1;i<=1000;i++)
	{
		if(flag[i]==1)
		{
			cnt++;
		}
	}
	cout<<"Total="<<cnt;
}

新年趣事之打牌

这里主要是看一下背包找构成数的方法。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=10020;
int f[N],w[N],vis;
int v[N],flag[N],s[N],cnt,n,m;
int main()
{
	cin>>m>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>v[i];
		w[i]=v[i];
	}
	f[0]=1; 
	for(int i=n;i>=1;i--)
	{
		for(int j=m;j>=v[i];j--)
		{
			if(f[j-v[i]]==1)
			{
				if(j==m)
				vis++;
				if(f[j]==0)
				{
					f[j]=1;
					s[j]=v[i];//追踪
				}
			}
		}
	}
	if(vis>1)
	{
		cout<<-1;
		return 0;
	}
	if(f[m]==0)
	{
		cout<<0;
		return 0;
	}
	int t=m;
	while(t)//标记
	{
		flag[s[t]]=1;
		t-=s[t];
	} 
	for(int i=1;i<=n;i++)
	{
		if(flag[v[i]]==0)//遍历
		{
			cout<<i<<" ";
		}
	}
}

背包拓展

1.有依赖的背包
相比于普通的01背包,这里的背包有了从属关系,买有些物品的前提是先买别的。
看一道例题

这是代码的主体部分

点击查看代码
cin>>sum;//买背包所需的价值 
			cin>>num;//可买物品的数量 
			for(int j=1;j<=num;j++)
			{
				cin>>v[j];
				cin>>w[j];
			}
			for(int j=0;j<sum;j++)
			{
				f[i][j]=-inf;//求最大,赋极小 
			}
			for(int j=sum;j<=m;j++)
			{
				f[i][j]=f[i-1][j-sum];
				//把上一个背包的值转移过来 
			}
			for(int j=1;j<=num;j++)
			{
				for(int k=m;k>=w[j];k--)
				{
					f[i][k]=max(f[i][k],f[i][k-v[j]]+w[j]);
				}
			}//正常求在该背包下的01背包 
			for(int j=0;j<=m;j++)
			{
				f[i][j]=max(f[i-1][j],f[i][j]);
			}//比较取最大值 

2.树上背包问题
有依赖的背包进阶版,详情可见我的树型DP。

3.求第k优的解

点击查看代码
#include<iostream>
#include<algorithm> 
#include<cstring> 
#include<cstdio> 
#include<cmath> 
using namespace std;
const int N=2500;
int n,m,k;
int f[N][N],v[N],w[N],a[N],b[N];
int cnt,num;
//f[i][j]表示在i的容积下的第j优解 
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		memset(f,0,sizeof(f));
		memset(w,0,sizeof(w));
		cnt=0;
		num=0;
		cin>>n>>m>>k;
		for(int i=1;i<=n;i++)
		{
			cin>>w[i];
		}
		for(int i=1;i<=n;i++)
		{
			cin>>v[i];
		}
		int i,j,l;
		for(i=1;i<=n;i++)
		{
			for(j=m;j>=v[i];j--)
			{
				for(l=1;l<=k;l++)
				{
					a[l]=f[j-v[i]][l]+w[i];
					b[l]=f[j][l];
				}
				a[l]=b[l]=-1;//防止越界 
				int x,y,z;
				x=y=z=1;
				while(z<=k&&(a[x]!=-1||b[y]!=-1))
				{
					if(a[x]>b[y])
					{
						f[j][z]=a[x];
						x++;
					}
					else
					{
						f[j][z]=b[y];
						y++;
					}
					//排序取值 
					if(f[j][z]!=f[j][z-1])
					//如果结果相同,舍去 
					{
						z++;
					}
				}
			}
		}
		cout<<f[m][k]<<endl;
	}
	
}

总结
背包就总结到这里了,拜拜。

标签:cnt,背包,int,cin,--,include,DP
From: https://www.cnblogs.com/zhengchenxi/p/18017996

相关文章

  • 关于动态规划(Dynamic Programming)的若干思考 ------ [1.背包dp]
    背包dp;1.01背包(1)领域展开#include<bits/stdc++.h>//simpleusingnamespacestd;constintmaxm=2024;intn,m;intw[maxm],v[maxm],f[maxm][maxm];intmain(){ cin>>n>>m; for(inti=1;i<=n;i++) cin>>v[i]>>w[i]; for(i......
  • 背包dp
    1、01背包f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i])二维为背包现有容量,一维为前i个物品表示在前i个物品所能选取的最大价值在判断第i个的最大值时要由前一个的状态转移过来;即下一层的状态由上一层转移来;可以直接省掉第一维(压维),从后往前更新过来,若还是正序就会出现一种情况......
  • dp的优化(单队,斜率)
    1.单调队列优化\(dp\)维护最小值:\(x\leqslantq.tail()\)维护最大值:\(x\geqslantq.tail()\)其实原理不难,当\(dp\)的转移源头是一个区间时,往往使用单调队列来维护区间最值(一般队列里装下标以方便维护区间大小,但也只是一般情况),节省了处理区间的时间(甚至噶掉一维),重点是对区间的......
  • 树状DP心得
    说一下近日所学的主要两种题型,一个是分叉情况问题,一种是树上背包问题。分叉情况问题具体的题可以参考小胖守皇宫和三色二叉树。点击查看代码小胖守皇宫#include<bits/stdc++.h>usingnamespacestd;constintN=2000;vector<int>son[N];intfa[N],h[N],f[N][3];//f[0]......
  • DPInst64.exe difxapi64.dll 是什么 为什么
    DPInst64.exe:安装和卸载驱动程序包。默认情况下,该工具将搜索当前目录,并尝试安装找到的所有驱动程序包。用法:C:\Users\Administrator\Desktop\dp\dp\DPInst64.exe[/UINF文件][/S|/Q][/LM][/P][/F][/SH][/SA][/A][/PATH路径][/EL][/L语言ID][/C][/D][/LogTitle标题][/SW][/?......
  • 空间转移(dp+倍增+Floyd)
    第2题   空间转移 查看测评数据信息给定一个n个点,m条边的有向图,编号从1到n,所有边权值是1,现在有一个动点从点1开始一动,其每秒钟可以移动2的k次方千米(k是任意自然数,且2的k次方不超过1000000000)。最少需要几秒才能到达n号点。数据保证1到n至少有一条路径。n⩽50,m⩽1000......
  • ThreadPoolTaskExecutor以及通过注解实现异步任务
    ThreadPoolTaskExecutor是Spring框架的线程池,实现方式如下:1//声明一个name为asyncTaskExecutor的线程池bean到容器中2@Bean("asyncTaskExecutor")3publicExecutorgetAsyncExecutor(){4ThreadPoolTaskExecutorthreadPoolExecutor=newThreadPoolTaskExecuto......
  • 状压 dp 与 插头 dp
    矩阵上的dp:按格dp/轮廓线dp设\(f[i,j,S]\)表示考虑到第\(i\)行第\(j\)个格子,轮廓线上所有的格子的状态。复杂度为\(\Theta(nm|S|)\)。按行dp\(f[i,S]\)表示选完前\(i\)行的合法方案数。总复杂度为\(\Theta(n|S|^2)\)。P3226[HNOI2012]集合选数给定集合......
  • [学习笔记]换根 DP 的常用处理方式
    [学习笔记]换根DP的常用处理方式换根DP,又称作二次扫描法,通常用于“对每个根求出树形DP的答案”。以每个点作为根节点进行一次树形DP的代价通常无法承受,因此我们会使用两次DFS:第一次DFS指定一个点为根节点,运行一次常规的树形DP。第二遍DFS进行换根DP,得到将根转移......
  • DP 做题记录
    复杂DP各种巨大DP。CF123CBrackets题意:括号数组是一个只有“(”或“)”两类字符的二维数组。括号数组中的合法路径只能从任意位置开始,向右或向下移动。如果一个n×m括号数组中从(1,1)到(n,m)的所有路径经过的字符构成的字符串均为可以完全匹配的括号序列,则这个括号......