演算法分析 棋盤型狀態壓縮dp 這類dp有一個通用的狀態表示法:f[i][j][k],表示前i行(放了j個棋子後)的狀態表示為k。 由於本題無棋子要求,因此可以省去中間一維, 即: 用f[i][j]表示前i行土地的狀態為j。 首先由於玉米地有不肥沃的地方不能種植,因此需要通過二進位表示出來可以種植和不 ...
演算法分析
棋盤型狀態壓縮dp
這類dp有一個通用的狀態表示法:f[i][j][k],表示前i行(放了j個棋子後)的狀態表示為k。
由於本題無棋子要求,因此可以省去中間一維,
即:
用f[i][j]表示前i行土地的狀態為j。
首先由於玉米地有不肥沃的地方不能種植,因此需要通過二進位表示出來可以種植和不可以種植的地方,
我們是將整行用一個二進位數表示的,可種為0,不可種為1,在輸入的時候即可判斷:
g[i] += (!x<<(j-1)) //g[i]為每一行土地的二進位表示
由於是棋盤型,因此根據我們的經驗顯而易見可以知道需要分別分析棋盤的列和行:
根據題意相鄰的兩格內不能同時種玉米可知:
(以x為當前格)則 (x&x>>1)=0;
這很容易理解,比如x的二進位表示為1101,則x>>1 = 0110,可以得出x&x>>1的結果和題目要求相同;
再分析每一列,由於上下列不能同時種玉米:
(y為相同列不同行的玉米地)因此(y&y-1)=0;
T.T好累
先看代碼吧:
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=13,mod = 1e8;
int f[N][1<<N];
int g[N];
vector<int> state;
vector<int> le_state[1<<N];
bool check(int x)
{
return !(x&x>>1);
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
int x;
scanf("%d",&x);
g[i]+=(!x<<(j-1));
cout<<g[i]<<endl;
}
}
for(int i=0;i<(1<<m);i++) {
if(check(i)) {
state.push_back(i);
}
}
for(int i=0;i<state.size();i++) {
for(int j=0;j<state.size();j++) {
if(!(state[i]&state[j])) {
le_state[i].push_back(j);
}
}
}
f[0][0]=1;
for(int i=1;i<=n+1;i++) {
for(int j=0;j<state.size();j++) {
if(state[j]&g[i]) continue;
for(int b=0;b<le_state[j].size();b++) {
f[i][j] = (f[i][j] + f[i-1][le_state[j][b]])%mod;
}
}
}
cout<<f[n+1][0];
return 0;
}