题解 P5074 【Eat the Trees】

SIGSEGV

2019-03-09 20:45:24

Solution

插头dp弱化版。 因为允许多个回路,所以没必要用括号序列(~~丧心病狂地用也没人阻止你~~)括号序列是为了防止提前闭合才使用的。本题直接存轮廓线上有无插头即可。 1. 左0上0=>下1右1(加转角) 2. 左1上1=>下0右0(闭合回路) 3. 左0上1=>下1右0(直走)下0右1(转弯) 4. 左1上0=>下1右0(转弯)下0右1(直走) 情况3,4本质相同,可以归在一类里 如果你不会插头dp,[右转模板题](https://www.luogu.org/problemnew/show/P5056) 上代码 ```cpp #include <bits/stdc++.h> using namespace std; int T,n,m,a[15][15]; const int MAX = 1 << 13; //m+1位 long long dp[2][MAX]; inline int query(const int &val,const int &pos) {return val & (1 << (pos - 1));} //查询val的第pos位 inline void ch(int &val,const int &pos,const int &vv) {val &= ~(1 << (pos - 1));val |= (vv << (pos - 1));} //把val的第pos位改成vv long long work() { scanf("%d%d",&n,&m); for (int i = 1;i <= n;i++) for (int j = 1;j <= m;j++) scanf("%d",&a[i][j]); memset(dp,0,sizeof(dp)); int now = 0,pre = 1;dp[pre][0] = 1;//初始化,dp滚动 for (int i = 1;i <= n;i++) { for (int j = 1;j <= m;j++) { for (int k = 0;k < (1 << (m + 1));k++) { int cur = k,left = query(k,j),up = query(k,j + 1); if (a[i][j] == 0) { if (!left && !up) dp[now][k] += dp[pre][k]; continue; } if (!left && !up) ch(cur,j,1),ch(cur,j + 1,1),dp[now][cur] += dp[pre][k];//情况1 else if (left && up) ch(cur,j,0),ch(cur,j + 1,0),dp[now][cur] += dp[pre][k];//情况2 else //情况3,4 { ch(cur,j,1),ch(cur,j + 1,0);dp[now][cur] += dp[pre][k]; ch(cur,j,0),ch(cur,j + 1,1);dp[now][cur] += dp[pre][k]; } } memset(dp[pre],0,sizeof(dp[pre])); swap(now,pre); } //每一行做完以后要迁移到下一行 for (int j = (1 << m) - 1;j >= 0;j--) { dp[now][j << 1] = dp[pre][j]; } memset(dp[pre],0,sizeof(dp[pre])); swap(now,pre); } return dp[pre][0];//最后的答案中轮廓线上不应该有插头 } int main () { scanf("%d",&T); while (T--) printf("%lld\n",work()); return 0; } ```