手推了一下,算是稍微明白了一点插头dp的原理

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 15;
const int maxm = 1<<15;
typedef long long ll;
int T, n, m, a[maxn][maxn];
int pre, cur, vis[maxm], total[2], state[2][maxm];
ll dp[2][maxm], ans;
void init() {
memset(a, 0, sizeof(a));
pre = -1, cur = 0;
total[0] = 1;
state[0][1] = 0;
dp[0][1] = 1;
ans = 0;
}
void addedge(int s, ll num) {
if (vis[s]) { dp[cur][vis[s]] += num; }
else {
vis[s] = ++total[cur];
state[cur][total[cur]] = s;
dp[cur][total[cur]] = num;
}
}
void plugdp() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
memset(vis, 0, sizeof(vis));
pre = cur; cur ^= 1;
total[cur] = 0;
for (int k = 1; k <= total[pre]; k++) {
int s = state[pre][k];
ll num = dp[pre][k];
int r = ((s&(1<<(j-1)))?1:0);
int d = ((s&(1<<j))?1:0);
if (a[i][j] == 0) {
if (r == 0 && d == 0) addedge(s, num);
}
else if (r == 0 && d == 0) {
if (a[i+1][j] == 1 && a[i][j+1] == 1) addedge(s+(1<<(j-1))+(1<<j), num);
}
else if (r == 0 && d == 1) {
if (a[i+1][j] == 1) addedge(s+(1<<(j-1))-(1<<j), num);
if (a[i][j+1] == 1) addedge(s, num);
}
else if (r == 1 && d == 0) {
if (a[i+1][j] == 1) addedge(s, num);
if (a[i][j+1] == 1) addedge(s-(1<<(j-1))+(1<<j), num);
}
else if (r == 1 && d == 1) {
addedge(s-(1<<(j-1))-(1<<j), num);
}
}
}
for (int j = 1; j <= total[cur]; j++) state[cur][j] <<= 1;
}
for (int i = 1; i <= total[cur]; i++) {
ans += dp[cur][i];
}
}
int main() {
scanf("%d", &T);
for (int kase = 1; kase <= T; kase++) {
scanf("%d%d", &n, &m);
init();
for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) scanf("%d", &a[i][j]);
plugdp();
printf("Case %d: There are %lld ways to eat the trees.\n", kase, ans);
}
return 0;
}