[Luogu P5056] [BZOJ 1814] 【模板】插头dp

本文介绍了一道洛谷和BZOJ上的经典插头DP题目,涉及在一个n*m的方格中,根据指定条件计算闭合回路的铺法总数。通过详细的代码解析,展示了如何使用插头DP的方法来高效解决这一问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洛谷传送门
BZOJ传送门

题目描述

给出 n ∗ m n*m nm的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路。问有多少种铺法?

输入输出格式

输入格式:

1 1 1行, n , m ( 2 ≤ n , m ≤ 12 ) n,m(2\le n,m\le 12) n,m(2n,m12)

从第 2 2 2行到第 n + 1 n+1 n+1行,每行一段字符串( m m m个字符)," ∗ * “表不能铺线,” . . ."表必须铺

输出格式:

输出一个整数,表示总方案数

输入输出样例

输入样例#1:
4 4
**..
....
....
....
输出样例#1:
2
输入样例#2:
4 4
....
....
....
....
输出样例#2:
6

解题分析

插头DP板子题, 这篇博客讲的很好, 就不再献丑了。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <cstdlib>
#include <tr1/unordered_map>
#define R register
#define IN inline
#define W while
#define ull unsigned long long
#define ll long long
#define MX 500500
#define uint unsigned int
int n, m, bdn, bdm, now, pre;
std::tr1::unordered_map <int, int> ind;
int tot[2], stat[2][MX], dgt[20];
ll val[2][MX], ans;
bool mp[20][20];
char buf[20][20];
IN void prep()
{
	for (R int i = 1; i <= m; ++i) dgt[i] = i << 1;
	for (R int i = 1; i <= n; ++i)
	for (R int j = 1; j <= m; ++j)
	if (buf[i][j] == '.') bdn = i, bdm = j, mp[i][j] = true;
}
IN void insert(R int hs, R ll v)
{
	int pos = ind[hs];
	if (!pos) pos = ind[hs] = ++tot[now], stat[now][pos] = hs;
	val[now][pos] += v;
}
int main(void)
{
	int i, j, k, st, lef, up, base, foo, bar, l;
	ll v;
	scanf("%d%d", &n, &m); now = 0, pre = 1;
	for (i = 1; i <= n; ++i) scanf("%s", buf[i] + 1);
	tot[0] = 1, val[0][1] = 1; prep();
	for (i = 1; i <= n; ++i)
	{
		for (j = 1; j <= m; ++j)
		{
			now ^= 1, pre ^= 1;
			std::memset(val[now], 0, (tot[now] + 1) * sizeof(ll));
			std::memset(stat[now], 0, (tot[now] + 1) * sizeof(int));
			ind.clear(); tot[now] = 0;
			for (k = 1; k <= tot[pre]; ++k)
			{
				st = stat[pre][k], v = val[pre][k];
				lef = (st >> dgt[j - 1]) % 4, up = (st >> dgt[j]) % 4;
				base = st ^ (lef << dgt[j - 1]) ^ (up << dgt[j]);
				if (!mp[i][j]) {if ((!lef) && (!up)) insert(st, v);}
				else
				{
					if (lef == 0)
					{
						if (up == 0)
						{
							if (mp[i + 1][j] && mp[i][j + 1])
							insert(base | (1 << dgt[j - 1]) | (2 << dgt[j]), v);//12
						}
						else if (up == 1)
						{
							if (mp[i + 1][j]) insert(base | (1 << dgt[j - 1]), v);//10
							if (mp[i][j + 1]) insert(st, v);//01
						}
						else
						{
							if (mp[i + 1][j]) insert(base | (2 << dgt[j - 1]), v);//20
							if (mp[i][j + 1]) insert(st, v);//02
						}
					}
					else if (lef == 1)
					{
						if (up == 0)
						{
							if (mp[i + 1][j]) insert(st, v);//10
							if (mp[i][j + 1]) insert(base | (1 << dgt[j]), v);//01
						}
						else if (up == 1)//11 -> 00, first 2 -> 1
						{
							foo = 1;
							for (l = j + 1; l <= m; ++l)
							{
								bar = (st >> dgt[l]) % 4;
								if (bar == 1) ++foo;
								if (bar == 2) --foo;
								if (!foo) {insert(base - (1 << dgt[l]), v); break;}
							}
						}
						else//12 -> 00, the end
						{
							if (i == bdn && j == bdm) ans += v;
						}
					}
					else
					{
						if (up == 0)
						{
							if (mp[i + 1][j]) insert(st, v); //20
							if (mp[i][j + 1]) insert(base | (2 << dgt[j]), v);//02
						}
						else if (up == 1) insert(base, v); //21 -> 00
						else//22 -> 00, first 1 -> 2
						{
							foo = 1;
							for (l = j - 2; l >= 1; --l)
							{
								bar = (stat[pre][k] >> dgt[l]) % 4;
								if (bar == 1) --foo;
								if (bar == 2) ++foo;
								if (!foo) {insert(base + (1 << dgt[l]), v); break;}
							}
						}
					}
				}
			}
		}
		for (l = 1; l <= tot[now]; ++l) stat[now][l] <<= 2;
	}
	printf("%lld", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值