有一个二维网格,然后里面每个位置要放一个 0~4 的数有一些已经填好的,也有要你填的。 然后一个位置可以跟相邻的四个点连边,然后要求一个点的边数量等于填的数字。 然后问你所有填写方案边的匹配方案数的平方的和。
方格填写
题目大意
有一个二维网格,然后里面每个位置要放一个 0~4 的数有一些已经填好的,也有要你填的。
然后一个位置可以跟相邻的四个点连边,然后要求一个点的边数量等于填的数字。
然后问你所有填写方案边的匹配方案数的平方的和。
思路
考虑这个平方的意义。
它其实是可以变成你选任意一种填数方式,在这中间任选两个匹配方案的方案数。
那我们就可以状压,因为看到状态很小,我们可以这样插头 DP:
fi,j,k,l 为当前搞到 (i,j) 的位置,两个方案的状态分别是 k,l。(k,l 是 m+1 位的状压,维护 m 个向下的,一个向右的)
然后转移一下即可,要注意的是要滚动数组。
代码
#include<cstdio>
#include<cstring>
#define ll long long
#define mo 998244353
using namespace std;
int T, n, m, a[71][7], aa[5], bb[5];
ll f[2][128][128];
int main() {
freopen("grid.in", "r", stdin);
freopen("grid.out", "w", stdout);
scanf("%d", &T);
while (T--) {
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(f, 0, sizeof(f));
f[0][0][0] = 1;
int now = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
if (a[i][j] != -1) {
for (int fr = 0; fr < (1 << (m + 1)); fr++) {
for (int frr = 0; frr < (1 << (m + 1)); frr++) {
int to = fr, too = frr;
int num = a[i][j], numm = a[i][j];
if (j == 1 && ((to >> m) & 1)) continue;
if (j == 1 && ((too >> m) & 1)) continue;
if ((to >> m) & 1) num--, to -= (1 << m);
if ((to >> (j - 1)) & 1) num--, to -= (1 << (j - 1));
if ((too >> m) & 1) numm--, too -= (1 << m);
if ((too >> (j - 1)) & 1) numm--, too -= (1 << (j - 1));
if (num < 0 || numm < 0 || num > 2 || numm > 2) continue;
if (!f[now ^ 1][fr][frr]) continue;
ll x = f[now ^ 1][fr][frr];
if (num == 0) aa[0] = 1, aa[1] = 0;
else if (num == 1) aa[0] = 2, aa[1] = (1 << m), aa[2] = (1 << (j - 1));
else if (num == 2) aa[0] = 1, aa[1] = (1 << m) | (1 << (j - 1));
if (numm == 0) bb[0] = 1, bb[1] = 0;
else if (numm == 1) bb[0] = 2, bb[1] = (1 << m), bb[2] = (1 << (j - 1));
else if (numm == 2) bb[0] = 1, bb[1] = (1 << m) | (1 << (j - 1));
for (int ii = 1; ii <= aa[0]; ii++)
for (int jj = 1; jj <= bb[0]; jj++)
(f[now][to | aa[ii]][too | bb[jj]] += x) %= mo;
}
}
}
else {
for (int qq = 0; qq <= 4; qq++) {
for (int fr = 0; fr < (1 << (m + 1)); fr++) {
for (int frr = 0; frr < (1 << (m + 1)); frr++) {
int to = fr, too = frr;
int num = qq, numm = qq;
if (j == 1 && ((to >> m) & 1)) continue;
if (j == 1 && ((too >> m) & 1)) continue;
if ((to >> m) & 1) num--, to -= (1 << m);
if ((to >> (j - 1)) & 1) num--, to -= (1 << (j - 1));
if ((too >> m) & 1) numm--, too -= (1 << m);
if ((too >> (j - 1)) & 1) numm--, too -= (1 << (j - 1));
if (num < 0 || numm < 0 || num > 2 || numm > 2) continue;
if (!f[now ^ 1][fr][frr]) continue;
ll x = f[now ^ 1][fr][frr];
if (num == 0) aa[0] = 1, aa[1] = 0;
else if (num == 1) aa[0] = 2, aa[1] = (1 << m), aa[2] = (1 << (j - 1));
else if (num == 2) aa[0] = 1, aa[1] = (1 << m) | (1 << (j - 1));
if (numm == 0) bb[0] = 1, bb[1] = 0;
else if (numm == 1) bb[0] = 2, bb[1] = (1 << m), bb[2] = (1 << (j - 1));
else if (numm == 2) bb[0] = 1, bb[1] = (1 << m) | (1 << (j - 1));
for (int ii = 1; ii <= aa[0]; ii++)
for (int jj = 1; jj <= bb[0]; jj++)
(f[now][to | aa[ii]][too | bb[jj]] += x) %= mo;
}
}
}
}
now ^= 1;
memset(f[now], 0, sizeof(f[now]));
}
printf("%lld\n", f[now ^ 1][0][0]);
}
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现