【bzoj1814】Ural 1519 Formula 1 插头dp
题目描述
一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数。
输入
The first line contains the integer numbers N and M (2 ≤ N, M ≤ 12). Each of the next N lines contains M characters, which are the corresponding cells of the rectangle. Character "." (full stop) means a cell, where a segment of the race circuit should be built, and character "*" (asterisk) - a cell, where a gopher hole is located.
输出
You should output the desired number of ways. It is guaranteed, that it does not exceed 2^63-1.
样例输入
4 4
**..
....
....
....
样例输出
2
题解
插头dp板子题
具体讲解可以参考 陈丹琦《基于连通性状态压缩的动态规划问题》 以及 Dalao博客 。
我的代码中使用了三进制状态,先使用dfs预处理出所有合法的广义括号序列,用0表示没有插头,1表示左括号插头,2表示右括号插头。处理好状态与编号的对应关系后进行dp,复杂度就只与合法状态数有关了。
时间复杂度 O(合法状态数·nm2) ,经计算合法状态数不超过42000。而实际上这个时间复杂度远远达不到上限,因为只有两个插头是双左括号或双右括号时才会产生最后的m,且这个m也是不满的。因此可以过。
注意需要long long。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | #include <cstdio> #include <algorithm> using namespace std; int m , a[14][14] , v[42000] , w[1600000] , tot , b[14]; long long f[13][13][42000]; char str[14]; void dfs( int p , int c , int now) { if (c < 0 || c > m + 1 - p + 1) return ; if (p > m + 1) { v[++tot] = now , w[now] = tot; return ; } dfs(p + 1 , c , now); dfs(p + 1 , c + 1 , now + b[p - 1]); dfs(p + 1 , c - 1 , now + 2 * b[p - 1]); } inline int left( int v , int p) { int i , c = 0; for (i = p ; ~i ; i -- ) { if (v / b[i] % 3 == 1) c -- ; if (v / b[i] % 3 == 2) c ++ ; if (!c) return i; } return -1; } inline int right( int v , int p) { int i , c = 0; for (i = p ; i <= m ; i ++ ) { if (v / b[i] % 3 == 2) c -- ; if (v / b[i] % 3 == 1) c ++ ; if (!c) return i; } return -1; } int main() { int n , i , j , k , nn , mm , p , q; long long ans = 0; scanf ( "%d%d" , &n , &m); for (i = 1 ; i <= n ; i ++ ) { scanf ( "%s" , str + 1); for (j = 1 ; j <= m ; j ++ ) if (str[j] == '.' ) a[i][j] = 1 , nn = i , mm = j; } b[0] = 1; for (i = 1 ; i <= m ; i ++ ) b[i] = b[i - 1] * 3; dfs(1 , 0 , 0); f[1][0][1] = 1; for (i = 1 ; i <= n ; i ++ ) { for (j = 1 ; j <= m ; j ++ ) { for (k = 1 ; k <= tot ; k ++ ) { p = v[k] / b[j - 1] % 3 , q = v[k] / b[j] % 3; if (!a[i][j]) { if (!p && !q) f[i][j][k] += f[i][j - 1][k]; } else { if (!p && !q && a[i][j + 1] && a[i + 1][j]) f[i][j][w[v[k] + b[j - 1] + 2 * b[j]]] += f[i][j - 1][k]; if (!p && q) { if (a[i][j + 1]) f[i][j][k] += f[i][j - 1][k]; if (a[i + 1][j]) f[i][j][w[v[k] + q * (b[j - 1] - b[j])]] += f[i][j - 1][k]; } if (p && !q) { if (a[i + 1][j]) f[i][j][k] += f[i][j - 1][k]; if (a[i][j + 1]) f[i][j][w[v[k] + p * (b[j] - b[j - 1])]] += f[i][j - 1][k]; } if (p == 1 && q == 1) f[i][j][w[v[k] - b[j - 1] - b[j] - b[right(v[k] , j)]]] += f[i][j - 1][k]; if (p == 2 && q == 2) f[i][j][w[v[k] - 2 * b[j - 1] - 2 * b[j] + b[left(v[k] , j - 1)]]] += f[i][j - 1][k]; if (p == 2 && q == 1) f[i][j][w[v[k] - 2 * b[j - 1] - b[j]]] += f[i][j - 1][k]; if (p == 1 && q == 2 && i == nn && j == mm) ans += f[i][j - 1][k]; } } } if (i != n) for (k = 1 ; k <= tot ; k ++ ) if (v[k] % 3 == 0) f[i + 1][0][k] += f[i][m][w[v[k] / 3]]; } printf ( "%lld\n" , ans); return 0; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!