牛客——德玛西亚万岁(状压DP)
题意:
已知一个n*m的01矩阵,0表示这个格子不可以站人,1表示这个格子可以站人。规定上下左右相邻的格子不能站人,问有多少种站人的方法。
思路:
因为n,m都很小,而且能够站人的人数也不是固定的。考虑状压枚举每一行的状态,进行转移。
在枚举状态的时候,有三种状态是不合法的。
1.当前的状态与地图的状态相矛盾;
2.当前状态本身不合法,即存在左右相邻的情况;
3.当前状态无法由上一个状态转移而来,即存在上下相邻的情况。
最后统计输出即可。
#pragma GCC optimize(2) #include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int>PII; #define I_int ll inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } char F[200]; inline void out(I_int x) { if (x == 0) return (void) (putchar('0')); I_int tmp = x > 0 ? x : -x; if (x < 0) putchar('-'); int cnt = 0; while (tmp > 0) { F[cnt++] = tmp % 10 + '0'; tmp /= 10; } while (cnt > 0) putchar(F[--cnt]); cout<<endl; } const int maxn=15,mod=100000000; int n,m; int dp[maxn][1<<16];///dp[i][j]表示第i行状态为j的方案数 int mp[maxn];///mp[i]表示第i行的地图状态 int main(){ while(~scanf("%d%d",&n,&m)){ memset(mp,0,sizeof mp);///初始化 memset(dp,0,sizeof dp);///初始化 dp[0][0]=1;///什么也不放,只有一种方案 for(int i=1;i<=n;i++) for(int j=0;j<m;j++){ int x;scanf("%d",&x); mp[i]|=(x<<j); } for(int i=1;i<=n;i++)///枚举当前在哪一行 for(int j=0;j<(1<<m);j++){///枚举当前这一行的状态 if((j&mp[i])!=j) continue;///当前这行的状态与地图的状态不符合 if(j&(j>>1)) continue;///左右有相邻的 for(int k=0;k<(1<<m);k++){///枚举上一行的状态 if(k&j) continue;///上下有相邻的 dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;///合法情况,可以由上一行的状态k转移过来 } } int res=0; ///统计并且输出答案 for(int i=0;i<(1<<m);i++) res=(res+dp[n][i])%mod; printf("%d\n",res); } return 0; }