原题:Ural1519
Ural1519:n*m有障碍矩阵,求单回路覆盖所有无障碍点方案数
/————————————————-
插头dp单回路裸模板题
用最小表示法表示连通性,需要MAXN/2+1进制,由于位运算方便快速,使用8进制
将每行可用状态存入HASH表中,
对于每个格子,
*非障碍格
***1.如果有左,上插头
(1)左上插头连通,连接两插头使得产生回路,只有在最后一个无障碍点才行
(2)左上插头不连通,易得左插头标号小于上插头,将上插头的连通块加入到左插头的连通块里
***2.如果只有左插头或者只有上插头
增加右插头或下插头到这个插头的连通块里
***3.没有左,上插头
增加右,下插头并使它们在新的连通块里
*障碍格
没有右,下插头
最后ans为最后一行所有可行状态的解的和
代码如下:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;
#define inf 0x7f7f7f7f
#define mod 1000000007
#define HASH 30001
#define N 15
#define STATE 1000100
typedef long long ll;
int n, m;
int code[N], ch[N];
int ex, ey;
int mp[N][N];
struct HASHMAP
{
int head[HASH], next[STATE], size;
ll state[STATE];
ll f[STATE];
void init() {
size = 0;
memset(next, -1, sizeof(next));
memset(head, -1, sizeof(head));
}
void push(ll st, ll ans) {
int h = st % HASH;
for (int i = head[h]; i != -1; i = next[i]) {
if (state[i] == st) {
f[i] += ans;
return;
}
}
state[size] = st;
f[size] = ans;
next[size] = head[h];
head[h] = size++;
}
}hm[2];
void decode(int * code, int m, ll st)
{
// cout<< "decode "<< m << ' ' << st << endl;
for (int i = m; i >= 0; i--) {
code[i] = st & 7;
st >>= 3;
}
}
ll encode(int * code, int m)
{
ll st = 0;
int cnt = 1;
memset(ch, -1, sizeof(ch));
ch[0] = 0;
for (int i = 0; i <= m; i++) {
if (ch[code[i]] == -1) ch[code[i]] = cnt++;
code[i] = ch[code[i]];
st <<= 3;
st |= code[i];
}
return st;
}
void shift(int * code, int m)
{
for (int i = m; i > 0; i--) {
code[i] = code[i-1];
}
code[0] = 0;
}
void dpblank(int r, int c, int cur)
{
for (int k = 0; k < hm[cur].size; k++) {
decode(code, m, hm[cur].state[k]);
int left = code[c-1];
int up = code[c];
if (up && left) {
if (up == left) {
if (r == ex && c == ey) {
code[c-1] = code[c] = 0;
if (c == m) shift(code, m);
hm[cur^1].push(encode(code, m), hm[cur].f[k]);
}
}
else {
code[c-1] = code[c] = 0;
for (int i = 0; i <= m; i++) {
if (code[i] == up) {
code[i] = left;
}
}
if (c == m) shift(code, m);
hm[cur^1].push(encode(code, m), hm[cur].f[k]);
}
}
else if (up || left) {
int t = up | left;//--
if (mp[r][c+1]) {
code[c] = t;
code[c-1] = 0;
if (c == m) shift(code, m);
hm[cur^1].push(encode(code, m), hm[cur].f[k]);
}
if (mp[r+1][c]) {
code[c-1] = t;
code[c] = 0;
if (c == m) shift(code, m);
hm[cur^1].push(encode(code, m), hm[cur].f[k]); }
}
else {
if (mp[r][c+1] && mp[r+1][c]) {
code[c-1] = code[c] = 7;
hm[cur^1].push(encode(code, m), hm[cur].f[k]);
}
}
}
}
void dpblock(int r, int c, int cur)
{
for (int k = 0; k < hm[cur].size; k++) {
decode(code, m, hm[cur].state[k]);
code[c-1] = code[c] = 0;
if (c == m) shift(code, m);
hm[cur^1].push(encode(code, m), hm[cur].f[k]);
}
}
int main()
{
while (~scanf("%d %d", &n, &m)) {
char t[100];
memset(mp, 0, sizeof(mp));
ex = ey = 0;
for (int i = 1; i <= n; i++) {
scanf("%s", t);
for (int j = 0; j < m; j++) {
if (t[j] == '.') {
mp[i][j+1] = 1;
ex = i;
ey = j+1;
}
}
}
if (!ex && !ey) {
puts("0");
continue;
}
int cur = 0;
hm[cur].init();
hm[cur].push(0, 1);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
hm[cur^1].init();
if (mp[i][j]) dpblank(i, j, cur);
else dpblock(i, j, cur);
cur ^= 1;
}
}
ll ans = 0;
for (int i = 0; i < hm[cur].size; i++) {
ans += hm[cur].f[i];
}
cout << ans << endl;
}
return 0;
}

本文详细解析了Ural1519问题的插头DP算法实现,针对n*m矩阵中存在障碍物的情况,通过最小表示法记录连通性,并采用8进制位运算进行高效的状态转移,最终计算出覆盖所有无障碍点的单回路方案数量。
1万+

被折叠的 条评论
为什么被折叠?



