题解:P7363 [eJOI2020 Day2] Dots and Boxes
posted on 2024-10-10 07:42:49 | under | source
很好的博弈题,出现在模拟赛中将我薄纱。
题意首先别看错了,两人轮流操作,假如一个人将一个格子四面都围起来了,称之为占领,那么就能继续操作。
然后我猜了个结论,假如能占领一个连通块的一个格子,那么就继续把这个连通分量给占满,因为剩下的不要白不要。
乍一看没问题,但是假如现在有个大小两个连通块,若一方直接把小连通块填满,那么就不得不去画大连通块,然后另一方就能把大连通块占领了,该贪心存在后效性。
基于这个例子,可以知道:先填连通块的不优,称之为先手,因为后手就能直接占领这个连通块;但是后手不一定要填满,因为之后就变成了其它连通块的先手,所以可以留下一些格子,不改变先后手。
由题意,连通块只能是链或简单环,先手先划一笔,后手有两种选择,对于链:
-
直接占领这个链,但是交换先后手。
-
让出一些格子给先手,不交换先后手。最少肯定是让 \(2\) 个,而让多了没有意义,相当于白给先手一些格子、还可能被反制而被迫交换先后手。
对于环同理,只不过要让 \(4\) 个格子。
那么现在有很多链(或环),先手应该选哪个?应该选最小的,因为不管咋选都会让给后手这个连通块,那么肯定先让出小的。
不过对于长度为 \(1\) 或 \(2\) 的链,情况不一样,因为先手占据了主动权,可以强行交换先后手(中间划一笔即可)。先手肯定想改变逆风局势成为更大连通块的后手,故一定先操作这些链(假如对链操作的话),而且肯定先选长度为 \(1\) 的,尽量少让出格子。
现在流程清晰了:先手选当前最小的链或环,后手选交不交换先后手顺序,以此类推;假如场上存在长为 \(1\) 或 \(2\) 的链,先手选链时优先选它们中较小者并交换先后手。
所以记 \(f_{i,j}\) 表示场上剩下前 \(i\) 大的链、前 \(j\) 大的环时,先手得分减去后手得分的最大值。注意到链的数量为 \(O(n+m)\) 级别(两端必然在边界上),所以复杂度 \(O(nm(n+m))\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define id(x, y) ((x - 1) * m + y)
#define MAX(a, b) a = max(a, b)
const int H = 25, NL = 45, NC = 405, inf = 1e7;
int n, m, cnt[H][H], sb;
char s[H];
vector<int> to[H * H];
bool vis[H * H], cir;
int siz, nl, l[NL], nc, c[NC], f[NL][NC];
inline bool chk(int x, int y) {return 1 <= x && x <= n && 1 <= y && y <= m;}
inline void add(int x, int y, int _x, int _y){
if(!chk(x, y) || !chk(_x, _y)) return ;
to[id(x, y)].push_back(id(_x, _y)), to[id(_x, _y)].push_back(id(x, y));
}
inline void dfs(int u, int from, bool &cir, int &siz){
vis[u] = true, ++siz;
for(auto v : to[u])
if(v ^ from){
if(!vis[v]) dfs(v, u, cir, siz);
else cir |= true;
}
}
inline bool cmp(int a, int b) {return a > b;}
signed main(){
cin >> n >> m;
for(int i = 1; i <= n + 1; ++i){
scanf("%s", s + 1);
for(int j = 1; j <= m; ++j)
if(s[j] == '0') add(i - 1, j, i, j);
else ++cnt[i][j], ++cnt[i - 1][j];
}
for(int i = 1; i <= n; ++i){
scanf("%s", s + 1);
for(int j = 1; j <= m + 1; ++j)
if(s[j] == '0') add(i, j - 1, i, j);
else ++cnt[i][j], ++cnt[i][j - 1];
}
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
if(!vis[id(i, j)] && cnt[i][j] < 4){
dfs(id(i, j), 0, cir = 0, siz = 0);
if(!cir) l[++nl] = siz;
else c[++nc] = siz;
}
sort(l + 1, l + 1 + nl, cmp), sort(c + 1, c + 1 + nc, cmp);
f[0][0] = 0;
for(int i = 0; i <= nl; ++i)
for(int j = (!i ? 1 : 0); j <= nc; ++j){
f[i][j] = -inf;
if(i){
if(l[i] <= 2) MAX(f[i][j], -l[i] - f[i - 1][j]);
else MAX(f[i][j], min(-l[i] - f[i - 1][j], -l[i] + 4 + f[i - 1][j]));
}
if(j) MAX(f[i][j], min(-c[j] - f[i][j - 1], -c[j] + 8 + f[i][j - 1]));
}
cout << f[nl][nc];
return 0;
}

浙公网安备 33010602011771号