P7363 [eJOI 2020 Day2] Dots and Boxes 题解
dengyaotriangle · · 题解
考虑这个局面的结构,发现由于每个点的度数为
考虑这个游戏的过程,第一步一定是先手在某一个连通块中加一条边,此时后手可以
-
以这条边为中心,不断加边直到占完这个连通块。然后最后选择一个其它的连通块的某条边,也就是转变为了少了一个连通块,自己先手的原问题的情况。
-
以这条边为中心,不断加边,但是在占的格子尽可能多的情况下不占满,逼迫先手继续回到先手的位置。而在后手回合结束后,先手没有任何选择,只能在把那些留下来的格子吃掉之后,自己再新开辟一个连通块。
第一种很好理解,就是尽可能多占当前的格子,而第二种呢?
首先,做第一种操作,注意到做完之后自己转变为了(一个相同子问题的)先手,而先手进行一次操作是得不到格子的,反而是后手拿到了,说明有时是需要避免自己成为先手的。这种时候需要在达到目的的情况下,尽可能占更多格子,也就是第二种策略的目标。
而具体方法,如图,红色为先手选的,后手按照顺序加蓝色的边,注意最后加的那条不会形成新格子,于是在此之后,后手的回合就结束了。
而且这种方法是最优的,也就是对于一个环最少剩
注意到剩下的部分先手继续操作的话一定会形成新格子,所以先手不能在不开辟新连通块的情况下结束自己的回合,只能在下一个连通块还是先手。
注意有一个特殊情况,对于一个长度
那么已经得到了一个关于连通块数指数级的算法,就是状压哪些集合被选了,然后先手枚举选哪个连通块,再枚举后手的策略,转移即可。
接下来,观察到,对于两个同种连通块(环/链),先手一定会选择其中更小的那个。
因为这样可以让后手得到的格子尽可能少,且(对于
(至于不同种类的连通块之间,注意到后手的选择一个最少剩
于是这两种连通块剩下的一定分别是一个后缀,所以可以设计 dp 状态
复杂度
#include<bits/stdc++.h>
using namespace std;
//dengyaotriangle!
const int maxn=25;
const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
const int maxm=405;
int n,m;
char _[maxn];
bool a[maxn][maxn][4];
bool vis[maxn][maxn];
int c[maxm],r[maxm],cs,rs;
int dp[maxm][maxm];//c: chain / r: ring
bool ic;
int sz;
void dfs(int x,int y){
sz++;
vis[x][y]=1;
for(int i=0;i<4;i++)if(a[x][y][i]){
int cx=x+dx[i],cy=y+dy[i];
if(cx>=1&&cx<=n&&cy>=1&&cy<=m){
if(!vis[cx][cy])dfs(cx,cy);
}else ic=1;
}
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n+1;i++){
cin>>_;
for(int j=1;j<=m;j++)if(_[j-1]=='0'){
a[i-1][j][2]=1;
a[i][j][3]=1;
}
}
for(int i=1;i<=n;i++){
cin>>_;
for(int j=1;j<=m+1;j++)if(_[j-1]=='0'){
a[i][j-1][0]=1;
a[i][j][1]=1;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(!vis[i][j]){
ic=0;sz=0;
dfs(i,j);
if(ic)c[++cs]=sz;
else if(sz>1)r[++rs]=sz;
}
}
}
sort(c+1,c+1+cs,greater<int>());sort(r+1,r+1+rs,greater<int>());
for(int i=0;i<=cs;i++){
for(int j=0;j<=rs;j++){
if(i==0&&j==0){dp[i][j]=0;continue;}
dp[i][j]=INT_MIN;
if(i){
int nw=c[i]+dp[i-1][j];//type1
if(c[i]>=3){
nw=max(nw,c[i]-2-(2+dp[i-1][j]));//type2
}
dp[i][j]=max(dp[i][j],-nw);
}
if(j){
int nw=max(r[j]+dp[i][j-1]/*type1*/,r[j]-4-(4+dp[i][j-1])/*type2*/);
dp[i][j]=max(dp[i][j],-nw);
}
}
}
cout<<dp[cs][rs];
return 0;
}