题解 P1879 【[USACO06NOV]玉米田Corn Fields】
Owen_codeisking
2018-07-18 13:44:56
## 前排警告
### 这是写给刚学的我看的,大佬请自动忽略
人生第一道状压$dp$ ~~互不侵犯是学术交流过的~~
本蒟蒻今天在机房听到状压$dp$,感觉很高大上……其实就是位运算高大上一点
#### 解释一下位运算:
& 给个例子
$0110101+1010011=0010001$,返回值为$17$
$n<<k$ 将$n$左移$k$位,相当于给$n$乘上$2^k$
$n>>k$ 将$n$右移$k$位,相当于给$n$除以$2^k$后向下取整
#### 解释一下状压$dp$:
它的基本思想就是用二进制来优美地暴力枚举出现的方案,
若二进制下第$i$位有赋值$1$,则一行的第$i$列有放牛
那么$f[i][j]$表示在前$i$行中(包括$i$)在$j$个状态下的最大方案数
易得$f[i][j]=(f[i][j]+f[i-1][k])\ mod\ p$($p=10^9$,$j$是第$i$行的状态,$k$是第$i-1$行的状态)
所以我们还要再预处理一下,$g[i]$表示第$i$个状态是否存在,判断条件是
$g[i]=$ $!(i$&$(i>>1))!(i$&$(i<<1))$
目标状态:$f[n][i]$全部相加
$Code \ Below:$
```cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int p=100000000;
ll f[13][1<<12],n,m;
ll g[1<<12],h[1<<12],a[13][13];
ll F[13];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
F[i]=(F[i]<<1)+a[i][j];
for(int i=0;i<(1<<m);i++){
if(!(i&(i>>1))&&!(i&(i<<1))){
g[i]=1;
if((i&F[1])==i) f[1][i]=1;
}
}
for(int x=2;x<=n;x++)
for(int j=0;j<(1<<m);j++)
if(((j&F[x-1])==j)&&g[j])
for(int k=0;k<(1<<m);k++)
if(((k&F[x])==k)&&!(j&k)&&g[k]){
f[x][k]=(f[x][k]+f[x-1][j])%p;
}
int ans=0;
for(int i=0;i<(1<<m);i++)
ans=(ans+f[n][i])%p;
printf("%d\n",ans);
return 0;
}
```