【LOJ 3378】点格棋(DP)(推论)

点格棋

题目链接:LOJ 3378

题目大意

有一个 (n+1)*(m+1) 的格点组成的网格,然后两个人轮流操作,选两个相邻(距离为 1)且没有连边的点对连一个竖直或者水平的线段。
然后如果一个人连线之后一个新的位置的四个边界都有线段了,那这个人就获得一分,并要继续操作。
然后无法操作时结束,然后给你当前的局势,问你从现在开始算分,先手的分减去后手的分的最大值。
保证当前局势满足每个格子的四个边界都有 2 个或者 4 个线段。

思路

会发现每个格子看做点,那也就是说每个点要么没有边,要么有两条边。
那就是形成了一些环,以及单点。
那单点在之前就贡献了,所以完全不用管。

但是其实不是,它有边界,那边界就相当于一条边,所以会有环和链。


那不如先考虑只有链怎么做。
那会发现先手一定会给后手创造机会,那如果只有一个连通块,那后手就能把所有的都取完。
那如果不止一个连通块,后手选完这个连通块的所有点,就要变成了先手,再开一个连通块。

不过会发现这样无法解释第二个样例,因为其实你不一定要选完。
那怎么让自己停下来呢?就是要少贪点,比如对于链我们可以留下一个大小为 2 的,如果是环,那就至少要留下两个大小为 2 的(也就是要留 4 的大小)

那显然多留是不优的,你肯定是在你能拿且保住后手的基础上尽量多拿,否则如果不保后手为啥不全取完。
那考虑一些特别的情况,比如链长度为 2,环大小为 4
发现如果链长度是 2,先手是可以不让后手保住后手的(就选中间的那个),但是环就没办法。

那我们就有状压 DP 是每个连通块是否处理,处理这些后手的最小费用(我用的是这个,也可以先手的最大费用)
那考虑优化,注意到对于所有的链,先手肯定会优先选长度最小的,因为先手是亏的。
那环也是这样。

那至少我们发现链,环,它们自己的顺序是固定的。
那就可以 fi,j 来 DP,复杂度就是 O(n2n2)(连通块数量是 n2

n=20 肯定能过。

代码

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 24; const int M = N * N; int n, m, id[N][N], tot, fa[M], sz[M]; bool lr[N][N], ud[N][N], cir[M]; int a[M], b[M], f[M][M]; int find(int now) { if (fa[now] == now) return now; return fa[now] = find(fa[now]); } void add(int x, int y) { if (find(x) == find(y)) {cir[find(x)] = 1; return ;} sz[find(y)] += sz[find(x)]; fa[find(x)] = find(y); } bool cmp(int x, int y) { return x > y; } int main() { scanf("%d %d", &n, &m); for (int i = 0; i <= n; i++) for (int j = 1; j <= m; j++) scanf("%1d", &lr[i][j]); for (int i = 1; i <= n; i++) for (int j = 0; j <= m; j++) scanf("%1d", &ud[i][j]); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { if (lr[i - 1][j] && lr[i][j] && ud[i][j - 1] && ud[i][j]) continue; id[i][j] = ++tot; } for (int i = 1; i <= tot; i++) fa[i] = i, sz[i] = 1; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { if (lr[i - 1][j] && lr[i][j] && ud[i][j - 1] && ud[i][j]) continue; if (i > 1 && !lr[i - 1][j]) add(id[i][j], id[i - 1][j]); if (j > 1 && !ud[i][j - 1]) add(id[i][j], id[i][j - 1]); } for (int i = 1; i <= tot; i++) if (fa[i] == i) if (cir[i]) b[++b[0]] = sz[i]; else a[++a[0]] = sz[i]; sort(a + 1, a + a[0] + 1, cmp); sort(b + 1, b + b[0] + 1, cmp); memset(f, 0x7f, sizeof(f)); f[0][0] = 0; for (int i = 0; i <= a[0]; i++) for (int j = 0; j <= b[0]; j++) { if (i < a[0]) { int val = a[i + 1] - f[i][j]; if (a[i + 1] > 2) val = max(val, (a[i + 1] - 2) - 2 + f[i][j]); f[i + 1][j] = min(f[i + 1][j], val); } if (j < b[0]) { int val = b[j + 1] - f[i][j]; if (b[j + 1] >= 4) val = max(val, (b[j + 1] - 4) - 4 + f[i][j]); f[i][j + 1] = min(f[i][j + 1], val); } } printf("%d", -f[a[0]][b[0]]); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/LOJ_3378.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2022-03-13 【luogu P4726】【模板】多项式指数函数(多项式 exp)(NTT)
2022-03-13 【luogu P4725】【模板】多项式对数函数(多项式 ln)(NTT)
2022-03-13 七十(环带状矩阵)(高斯消元)
2021-03-13 【luogu P6091】【模板】原根
2021-03-13 【ybt金牌导航8-6-5】最小原根
点击右上角即可分享
微信分享提示