背包dp训练总结

背包dp分类:

01背包dp

完全背包(飞扬的小鸟)

多重背包

二维费用背包

分组背包()

有依赖的背包(难的还是不好解决,虽说是在每一个依赖中做一次01背包,但感觉不好实现)




做背包问题最关键的就是找清楚并反问自己?这题里面     什么是容量?   什么是物品?  什么是物品的费用?   什么是物品的价值

容量,就是这题当中我们怎样表示状态的数组。

费用,就是用来f【i】---->f【i+v【k】】,状态转移的跨度

价值,就是你这个dp的数组,所维护的东西。维护的数值!



例如下面这道题:求给所有的人安排房间最小支出是多少?  一般这样问的,那么所有人在这里就表示整个dp的数组,多个房间就是物品,最小支出就是数组里存的价值。根据感觉,只要找出背包问题中的物品(也就是转移的方向)背包模型就好办了


vijos1240朴素的网络游戏


首先我们要明确,对于一道背包dp的题目来说,我们需要有容量,物品,费用,价值(权值,因为有些题要求最小),例如这个题:每一个房间能撑下多少人就是费用,因为最关键的我们一共要把所有的人乘下,才行。那么每加一个房间就会转移到下一个状况,更有,因为我们要求总价值最小,就是画的钱最少



vijos1037 搭建双塔

背包位运算的优化,bitset的实践

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<bitset>
using namespace std;
int w[105],n,h[105],sum=0;

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)scanf("%d",&w[i]),sum+=w[i];
	bitset<3000> f[3000];
	for (int i=0;i<=sum;i++) f[i].reset();
	f[0].set(1);
	for (int i=1;i<=n;i++)
	{
		for (int j=sum;j>=0;j--) if (f[j].any())
		{
			f[j+w[i]]=f[j]|f[j+w[i]];//位运算的关键
			f[j]=f[j]|(f[j]<<w[i]);//关键,位移运算
		}
	}
	int ans;
	for (ans=sum;ans>0;ans--) if (f[ans].test(ans+1)) break;
	if (ans) printf("%d",ans);else printf("Impossible");
	return 0;
}

这是没有加位运算的,这样更直观一点,dp【i】【j】表示第一个塔高为i,第二个高为j的是否存在,输出时判断最大的i==j的输出

#include<cstdio>
#include<algorithm>
using namespace std;
int a[105],n;
bool dp[5000][5000];
int main()
{
	int n;
	scanf("%d",&n);
	int sum=0;
	for (int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
	dp[0][0]=true;
	for (int i=1;i<=n;i++)
	{
		for (int j=sum;j>=0;j--)
		for (int l=sum;l>=0;l--)
		if (dp[j][l])
		{
			dp[j+a[i]][l]=true;
			dp[j][l+a[i]]=true;
		}
	}
	int ans;
	for(ans=sum;ans>0;ans--)if (dp[ans][ans]) break;
	if (ans)printf("%d",ans);else printf("Impossible");
	return 0;
}
vijos1198最佳课程选择(锻炼了一下,找背包的模型)这里用long long

#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<iostream>
#define inf 0x7f7f7f7f 
#define ll long long
using namespace std;
ll f[3090],a[550],b[550],n,m;
ll power(int a,int b)
{
	ll ans=1;
	while (b--) ans*=a;
	return ans;
}
int main()
{
	scanf("%lld%lld",&n,&m);
	for (int i=1;i<=m;i++) scanf("%lld%lld",&a[i],&b[i]);
	memset(f,inf,sizeof(f));
	f[0]=0;
	for (int i=1;i<=m;i++)
	{
		for (int j=n-1;j>=0;j--) if (f[j]!=inf)
		for (int l=1;l<=n-j;l++) 
		f[j+l]=min(f[j+l],f[j]+a[i]*power(l,b[i]));
	}
	printf("%lld",f[n]);
	return 0;
}


vijos1250 最勇敢的机器人 (并查集预处理+分组背包dp)

#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>

using namespace std;
const int maxn=1022;
int n,w[maxn],p[maxn],wm,k,fa[maxn],hh[maxn],last[maxn],head[maxn],da[maxn],f[maxn];
bool b[maxn]={false};
int find(int x)
{
	if (fa[x]==x)return x;
	fa[x]=find(fa[x]);
	return fa[x];
}
int main()
{
	scanf("%d%d%d",&n,&wm,&k);
	for (int i=1;i<=n;i++) scanf("%d%d",&p[i],&w[i]);
	for (int i=1;i<=n;i++) fa[i]=i;
	while (k--)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		int af=find(a),bf=find(b);
		if (af!=bf) fa[bf]=af;
	}
	int k;
	for (int i=1;i<=n;i++) k=find(i);
	memset(head,0,sizeof(head));
	for (int i=1;i<=n;i++) 
	{
		last[i]=head[fa[i]];head[fa[i]]=i;
		if (hh[fa[i]]==0){hh[fa[i]]=1;da[++da[0]]=fa[i];}
	}
	for (int i=1;i<=da[0];i++)
	{
		for (int j=wm;j>=0;j--) 
		for (int l=head[da[i]];l;l=last[l])
		f[j+w[l]]=max(f[j+w[l]],f[j]+p[l]);
	}
	printf("%d",f[wm]);
	return 0;
}
vijos1421多人背包(01背包前k优解)
本题其实是求01背包的前k优解之和,归并一下就好
f[i][j] 表示容量为 i 时第 j 优解的值,注意状态转移时要归并,归并的应用!!!!
(关于题目中所述的“背包必须装满”可以这样处理:把数组全部赋 -INF,特殊的是 f[0][0] = 0,这样一来最后
f[capacity][1..k] 中的负数取值就意味着无法装满。)

这是一种思路,不用我再用另一个bool数组判断容量i时是否有没有,不过另一个数组记录更加直观一些

#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdio>
using namespace std;

int k,v,n,f[5009][55],s[209],w[209];

int main()
{
	scanf("%d%d%d",&k,&v,&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&s[i],&w[i]);
	memset(f,-0x3f3f3f3f,sizeof(f));
	f[0][1]=0;
	for (int i=1;i<=n;i++)
	{
		for (int j=v;j>=s[i];j--)
		{
			int cnt[55],l=1,r=1;
			for (int ll=1;ll<=k;ll++)
			if (f[j-s[i]][l]+w[i]>f[j][r]) cnt[ll]=f[j-s[i]][l]+w[i],l++;
			else  cnt[ll]=f[j][r],r++;//归并,状态转移
			for (int ll=1;ll<=k;ll++) f[j][ll]=cnt[ll];
		}
	}
	int ans=0;
	for (int i=1;i<=k;i++) ans+=f[v][i];
	printf("%d",ans);
	return 0;
}


做背包问题最关键的就是找清楚并反问自己?这题里面     什么是容量?  什么是费用?   什么是价值

容量,就是这题当中我们怎样表示状态的数组。

费用,就是用来f【i】---->f【i+v【k】】,状态转移的跨度

价值,就是你这个dp的数组,所维护的东西。维护的数值!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值