状压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;
}

浙公网安备 33010602011771号