算法 动态规划 状压DP

本文介绍了状态压缩的概念,它是通过二进制位运算优化动态规划的一种方法。以一个n层楼开窗户的问题为例,说明如何用二进制表示状态,并应用于动态规划。接着讲解了如何结合动态规划形成状压DP,并给出了USACO06NOV的玉米田问题作为例题,解释了如何运用状压DP求解问题。


状压DP,全程状态压缩动态规划
核心便是状态压缩。。。。。

状态压缩简介

状态压缩,是利用计算机二进制的性质来描述状态的一种DP方式,经常与BFS和DP连用
举个不恰当的栗子
有一个n层楼的楼房,每一层可以选择开窗户或者不开窗户
令n=6
有二进制数101101,每一位表示这一层楼是否开了窗户,1 表示开了,0表示没开,我们就能利用位运算快速地得知1,3,4,6层楼开了窗
所以说状态压缩可以说得上是用位运算优化后的暴力

状压DP简介

状态压缩+动态规划=状压DP

例题

P1879[USACO06NOV]玉米田Corn Fields
原题链接
https://www.luogu.org/problem/P1879

读入以后,用f[i]来表示第i行上草地的情况,f[]是boolean

我们需要在0~max_state-1里找到合法状态(max_state代表这道题的最大状态)判断方法就是吧这个二进制数左移一位,然后右移一位。若这个状态是合法的,就return 0

然后开始DP,从第一行开始,在每行里面罩所欲的状态,若这个状态是合法的,且不在不适合种草的草地上面,那么接下来开始找一行合法的情况,吧上一行的情况加到f[i][j]里

最后吧末尾行的情况数全部加起来就是答案了

#include <cstdio>
#include <iostream>
const int maxn=4096,mod=100000001;
int n,m;
int tmap[19][19];
int dmap[19];
int dp[19][maxn];
bool can[maxn];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>tmap[i][j];
			map[i]=(map[i]<<1)+tmap[i][j];
		}
	}
	int maxstate=(1<<m)-1;
	for(int i=0;i<=maxstate;i++)
		if((((i<<1) and i)==0) and (((i>>1) and i)==0))
			can[i]=true;
	for(int i=0;i<=maxstate;i++)
		if(can[i] and ((i and dmap[1])==i))
			dp[1][i]=1;//先预处理处第一行(对于某一行的状态,只受上一行的影响)
	for(int i=2;i<=n;i++)
		for(int j=0;j<=maxstate;j++)
			if(can[j] and (j and dmap[i])==j)
				for(int k=0;k<=maxstate;k++)
					if(k and j==0)
						dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;//DP
	long long ans=0;
	for(int i=0;i<=maxstate;i++)
		ans=(ans+dp[n][i])%mod;//答案在最后一行
}

JZOJ1236邦德I

#include <cstdio>
#include <iostream>
using namespace std;
double a[20][20];
int b[20],c[20];
double f[2500001];
int max(int x, int y)
{
	return x>y?x:y;
}
int power(int x, int y)
{
	int ans=1;
	for(int i=1;i<=y;i++)
		ans*=x;
	return ans;
}
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			cin>>a[i][j];
			a[i][j]/=100;
		}
	f[0]=100;
	for(int i=1;i<=power(2,n+1)-1;i++)
	{
		int t=0;
		for(int j=1;j<=n;j++)
			if(i and b[j]!=0)
			{
				t+=1;
				c[t]=j;
			}
		for(int j=1;j<=t;j++)
			f[i]=max(f[i=b[c[j]]]*a[c[j]][t],f[i]);
	}
	cout<<f[power(2,n+1)-1];
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值