题意:
一块玉米地,有的位置不能种草,种草的小方格不能有临边. 问有多少种方案.
思路:
状压DP.
dp[ i ][ j ] 表示从上到下处理到第 i 行时, 该行状态为 j 的方案数.
下一行某状态的方案数就是上一行所有合理状态的方案数之和.
注意初始化和最后一行的处理.
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 14;
const int MOD = 1e8;
typedef long long ll;
int pat[MAXN],m,n;
ll dp[MAXN][1<<MAXN];
inline bool valid(int s,int i)
{
return (s & (~pat[i]))?false:true;
}
inline bool adj(int s)
{
for(int i=0;i<n-1;i++)
{
if((1<<i)&s && (1<<(i+1))&s) return true;
}
return false;
}
void DP(int i, int s)
{
for(int j=0;j<(1<<n);j++)
{
if(!valid(j,i-1) || (s&j) || adj(j)) continue;
dp[i][s] += dp[i-1][j];
}
dp[i][s] %= MOD;
}
int main()
{
scanf("%d %d",&m,&n);
dp[0][0] = 1;
for(int i=1;i<=m;i++)
{
int t;
for(int j=n-1;j>=0;j--)
{
scanf("%d",&t);
if(t) pat[i] += 1<<j;
}
for(int j=0;j<(1<<n);j++)
{
if(!valid(j,i)||adj(j)) continue;
DP(i,j);
}
}
DP(m+1,0);
printf("%d",(int)dp[m+1][0]);
}
131007重写:
行标号1-m, 列标号1-n.
dp[i][state]表示处理了1-i行时, 且第i行为state排布时, 一共的方案数.
可以枚举前面的更新后面的, 也可以枚举后面的然后找前面可行的加起来...第一种其实是不需要的, 因为前面不可行的方案数自然是0, 不判断的话也没有影响.
后面的需要满足:
1. 横向不相邻(不同时为1) 2. pat中0的位置不可取(&==0) 3. 与上一行不相邻(~&==0)
先找出后面的可行状态, 再Σ前面不矛盾的状态的方案数. 因为后面的状态需要额外判断本身, 因此枚举后面的.
864K 16MS ..效率低了..
上面的是
440K 0MS...
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 15;
const int MOD = 100000000;
int n,m,pat0,pat[MAXN];
ll dp[MAXN][1<<12];
int main()
{
while(scanf("%d %d",&m,&n)==2)
{
memset(pat,0,sizeof(pat));
for(int i=1;i<=m;i++)
for(int j=0;j<n;j++)
{
scanf("%d",&pat0);
if(!pat0) pat[i] |= 1<<j;//1==forbiden
}
memset(dp,0,sizeof(dp));
dp[0][0] = 1;
for(int i=1;i<=m;i++)//枚举行
for(int state=0;state<1<<n;state++)//枚举当前状态
if((state&pat[i])==0)//符合题设
{
int k;
for(k=1;k<n;k++)//横向不相邻
if(((state>>k)&1) & ((state>>(k-1))&1)) break;
if(k!=n) continue;
for(int s=0;s<1<<n;s++)//枚举上一行可行的
if(dp[i-1][s] && (state&s)==0)
dp[i][state] += dp[i-1][s];
}
for(int s=0;s<1<<n;s++)
dp[m+1][0] += dp[m][s];
printf("%d\n",(int)(dp[m+1][0]%MOD));
}
}
调用函数判断大概比较快><