**队友用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;
}