**隊友用dfs過了,回來學了發輪廓線dp,現學現賣。
看過輪廓線後,覺得這玩意兒好神奇,寫起來比dfs順手多了,雖然兩者的思想其實都是差不多的,都是從一個合法狀態通過邊轉移到另一個狀態,dfs難寫,長,輪廓線好寫,容易寫錯。思路必須要清晰。
不說廢話了,輪廓線的經典之處在於每次枚舉舊的狀態。通過舊的狀態走到新狀態的時候,舊的狀態先左移一位,再建立新狀態,通過這樣移位的操作,就不用傻傻的在最內層循環通過dfs來搜狀態進行轉移了,直接位運算搞定。每次更新的都是第m-1位(假設行爲n,列爲m的話),
並且在m-1位上方的點就是(sta&(1<<m-1)) sta表示舊狀態,在m-1左邊一個位置的點就是第0位,具體看代碼轉移**
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <sstream>
#include <set>
#include <vector>
#include <stack>
#define ALL(x) x.begin(), x.end()
#define INS(x) inserter(x, x,begin())
#define ll long long
#define CLR(x) memset(x, 0, sizeof x)
using namespace std;
const int inf = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 1e2 + 10;
const int maxv = 1e1 + 10;
const double eps = 1e-9;
int n, m, c, d;
int mp[maxn][maxv];
int state[maxn];
int cur;
ll dp[2][(1 << 10) + 10][25];
void update(int a, int b, int c, int d) {
if(b & (1 << m)) dp[cur][b^(1<<m)][c] = (dp[cur][b^(1<<m)][c] + dp[1-cur][a][d]) % MOD;
}
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif
while(scanf("%d%d%d%d", &n, &m, &c, &d) != EOF) {
CLR(state); CLR(dp);
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
scanf("%1d", mp[i] + j);
mp[i][j] ^= 1;
state[i] = state[i] * 2 + mp[i][j];
}
}
dp[0][(1 << m) - 1][0] = 1;
cur = 0;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
cur ^= 1;
CLR(dp[cur]);
for(int sta = 0; sta < (1 << m); sta++) {
for(int k = 0; k <= d; k++) {
if(mp[i][j]) {
update(sta, (sta << 1) ^ 1, k, k);
continue;
}
update(sta, sta << 1, k, k);
if(k != d)
update(sta, (sta << 1) ^ 1, k+1, k);
if(i && !(sta & (1 << m - 1)))
update(sta, (sta << 1) ^ (1 << m) ^ 1, k, k);
if(j && !mp[i][j-1] && !(sta & 1))
update(sta, (sta << 1) ^ 3, k, k);
}
}
}
}
ll ans = 0;
for(int i = c; i <= d; i++) {
ans = (ans + dp[cur][(1 << m) - 1][i]) % MOD;
}
printf("%lld\n", ans);
}
return 0;
}