状压DP

状态压缩DP

蒙德里安的梦想

求把N*M的棋盘分割成若干个1x2的的长方形,有多少种方案。

例如当N=2,M=4时,共有5种方案。当N=2,M=3时,共有3种方案。

如下图所示:

2411_1.jpg

输入格式
输入包含多组测试用例。

每组测试用例占一行,包含两个整数N和M。

当输入用例N=0,M=0时,表示输入终止,且该用例无需处理。

输出格式
每个测试用例输出一个结果,每个结果占一行。

数据范围
1≤N,M≤11
输入样例:

1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0

输出样例:

1
0
1
2
3
5
144
51205
1. **所谓的状态压缩DP,就是用二进制数保存状态。为什么不直接用数组记录呢?因为用一个二进制数记录方便作位运算。前面做过的八皇后,八数码,也用到了状态压缩。**

2. **本题等价于找到所有横放 1 X 2 小方格的方案数,因为所有横放确定了,那么竖放方案是唯一的。**

3. **用$f[i][j]$记录第i列第j个状态。j状态位等于1表示上一列有横放格子,本列有格子捅出来。转移方程很简单,本列的每一个状态都由上列所有“合法”状态转移过来$f[i][j] += f[i - 1][k]$**

4. **两个转移条件: i 列和 i - 1列同一行不同时捅出来 ; 本列捅出来的状态j和上列捅出来的状态k求或,得到上列是否为 奇数空行状态,奇数空行不转移。**

5. **初始化条件$f[0][0] = 1$,第0列只能是状态0,无任何格子捅出来。返回$f[m][0]$。第m + 1列不能有东西捅出来**
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 12, M = 1 << N;//M的每一位二进制位储存一种状态

int n, m;
long long f[N][M];
bool bz[M];//储存每一列上合法的摆放状态


int main()
{
	while(~scanf("%d %d",&n,&m))
	{
		if(!n&&!m) return 0;
		
		for(int i=0;i<(1<<m);++i)
		{
			int cnt=0;
			bz[i]=1;
			for(int k=0;k<m;++k)
			{
				if(i>>k&1)
				{
					if(cnt&1){bz[i]=0;break;} 
					cnt=0;
				}
				else cnt++;
			}
			if(cnt&1) bz[i]=0;
		}

		memset(f,0,sizeof(f));
		f[0][0]=1;
		
		for(int i=1;i<=n;++i)
		for(int j=0;j<(1<<m);++j)
		for(int k=0;k<(1<<m);++k)
		if((j&k)==0&&bz[j|k])
			f[i][j]+=f[i-1][k];
	
		printf("%lld\n",f[n][0]);
	}
}

炮兵阵地

司令部的将军们打算在NM的网格地图上部署他们的炮兵部队。一个NM的地图由N行M列组成,地图的每一格可能是山地(用”H” 表示),也可能是平原(用”P”表示),如下图。

在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

1185_1.jpg

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。

图上其它白色网格均攻击不到。

从图上可见炮兵的攻击范围不受地形的影响。

现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

输入格式
第一行包含两个由空格分割开的正整数,分别表示N和M;

接下来的N行,每一行含有连续的M个字符(‘P’或者’H’),中间没有空格。按顺序表示地图中每一行的数据。

输出格式
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。

数据范围
N≤100,M≤10
输入样例:

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

输出样例:

6
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
#define maxn 105
#define maxm 65

string s;
int n,m;
int ans,f[maxn][maxm][maxm];
deque<int>hf[maxn],cnt[maxn];

void ycl()
{
	hf[0].push_back(0);
	for(int i=1;i<=n;++i)
	{
		cin>>s;
		for(int k=0;k<(1<<m);++k)
		{
			int t=0,last=-3;
			bool bz=1;
			for(int j=0;j<m;++j)
			{
				if(k>>j&1)
				{
					if(s[j]=='H'||j-last<3){bz=0;break;}
					//布置在山地或者距离不足 
					t++;
					last=j;
				}
			}
			
			if(bz)
			{
				hf[i].push_back(k);
				cnt[i].push_back(t);
			}
		}
	}
}

int main()
{
	scanf("%d %d",&n,&m);
	ycl();
	
	memset(f,0x80,sizeof(f));
	
	f[0][0][0]=0;
	for(int i=0;i<hf[1].size();i++)	f[1][i][0]=cnt[1][i];
	//预处理0和1行
	 
	for(int i=2;i<=n;++i)
	{
		for(int j=0;j<hf[i].size();++j)
		{
			int u=hf[i][j],count=cnt[i][j];
			//u:本行的状态 count:本状态下的炮兵 
			for(int k=0;k<hf[i-1].size();++k)
			{
				//枚举上一行的状态 
				int v=hf[i-1][k];
				if(u&v) continue;
				//如果两行状态有重叠,跳过 
				for(int s=0;s<hf[i-2].size();++s)
				{
					//枚举上上行的状态 
					int w=hf[i-2][s],val=f[i-1][k][s];
					 if(u&w||val<0) continue;
					//如果本行和上上行状态重叠||上行转移不合法,跳过
					f[i][j][k]=max(f[i][j][k],val+count);
				}
		
			}
		
		}
	}
	
	for(int i=0;i<hf[n].size();++i)
	for(int j=0;j<hf[n-1].size();++j)
	ans=max(ans,f[n][i][j]); 
	
	printf("%d",ans);
}

宝藏

#include<bits/stdc++.h>
using namespace std;

#define ll long long

const int N=1e4+5;
const ll INF=5e5*12*12;
ll n,m,val,ans,d[15][15],dp[13][1<<12];

inline ll read()
{
	char dd=getchar();
	ll res=0,w=1;
	while(dd<'0'||dd>'9')
	{
		if(dd=='-') w=-1;
		dd=getchar();
	}
	while(dd>='0'&&dd<='9') res=(res<<1)+(res<<3)+dd-'0',dd=getchar();
	return res*w;
}

ll cost(int now,int nxt)
{
	ll res=0;
	
	for(int j=0;j<n;++j)
	if((1<<j)&nxt)
	{
		ll x=d[0][0];
	
		for(int i=0;i<n;++i)
		if((1<<i)&now) x=min(x,d[i][j]);
		
		if(x==d[0][0]) return -d[0][0];
		res+=x;
	}
	
	return res;
}
int main()
{
	n=read();m=read();
	memset(d,0x3f,sizeof(d));
	
	for(int i=1,u,v;i<=m;++i) 
	u=read()-1,v=read()-1,d[u][v]=d[v][u]=min(d[u][v],read());
	
	memset(dp,0x3f,sizeof(dp));
	
	for(int i=0;i<n;++i) dp[0][1<<i]=0;
	
	ans=dp[0][0];
	
	for(int i=0;i<n;++i)
	for(int j=0;j<(1<<n);++j)
	{
		if(dp[i][j]==dp[0][0]) continue;
		if(j==(1<<n)-1)
		{
			ans=min(ans,dp[i][j]);
			continue;
		}
		for(int k=j+1;k<(1<<n);++k)
		if((j&k)==j&&(val=cost(j,k-j))!=-d[0][0])
			dp[i+1][k]=min(dp[i+1][k],dp[i][j]+val*(i+1));
	}
	cout<<ans;
}
posted @ 2020-10-23 19:31  林生。  阅读(57)  评论(0)    收藏  举报