2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) G - Maximum Islands

2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) G - Maximum Islands

七月 30, 2021

题目概述

给一张表格,表格上L代表陆地,W代表水域,C代表未知(可以使陆地也可以使水域)。现在可以将C区域变成陆地或水域,问最多陆地的连通块有多少块?

数据范围

分析与解

首先贪心上应该把L周围的区域变成水域,然后再考虑剩下的C点。我们希望尽可能多的填上L,因此最好相邻的格子填不同的区域。

所以问题就转化成一个二分图求最大独立集的问题(见下文)。考虑将原表格从上到下从左到右黑白染色划分格子的归属(说白了就是行号和列号奇偶性相同为黑色,不同为白色),然后对于C点相邻连边,跑最大匹配即可。

最后答案就是原本贪心后已有的L连通块个数 + C点数 - 最大匹配数。

图论相关概念回顾

独立集:无向图点集的一个子集,其中任意两点不相连。

匹配:无向图边的一个子集,该子集任意两条边没有公共顶点且没有自环

顶点覆盖:无向图点集的子集,使得边集中任意一条边都至少有一个点在该子集中。

性质

|最小顶点覆盖| + |最大独立集| = |V|

在二分图中,最小顶点覆盖 = 最大匹配,因此最大独立集 = 点数 - 最大匹配数。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <bits/stdc++.h>
using namespace std;
int mread() {
int ret=0,f=1;
char c=getchar();
while(c<'0' || c>'9') {
if(c=='-')f=-1;
c=getchar();
}
do {
ret=ret*10+c-'0';
c=getchar();
} while('0'<=c && c<='9');
return ret*f;
}
#define MAXN 100100
struct node {
int v;
node *nnext;
} pool[MAXN<<1],*h[MAXN<<1];
int cnt=-1;
void addedge(int u,int v) {
// printf("%d %d\n",u,v);
node *p=&pool[++cnt];
p->v=v,p->nnext=h[u],h[u]=p;
node *q=&pool[++cnt];
q->v=u,q->nnext=h[v],h[v]=q;
}
int n,m;
char mp[45][45];
int dx[4]= {0,0,-1,1},dy[4]= {-1,1,0,0};
bool vis[45][45];
bool check(int x,int y) {
if(1<=x && x<=n && 1<=y && y<=m)return true;
return false;
}
void dfs(int x,int y) {
vis[x][y]=true;
int nx,ny;
for(int i=0; i<4; i++) {
nx=x+dx[i],ny=y+dy[i];
if(check(nx,ny) && !vis[nx][ny] && mp[nx][ny]=='L')
dfs(nx,ny);
}
}
vector<int> lef,rig;
int connect[2000],state[2000];
bool match(int u) {
int v;
for(node *p=h[u]; p; p=p->nnext) {
v=p->v;
if(state[v]==0) {
state[v]=1;
if(connect[v]==0 || match(connect[v])) {
connect[v]=u;
return true;
}
}
}
return false;
}
int hungary() {
int sum=0;
for(auto now : lef) {
memset(state,0,sizeof(state));
if(match(now))
sum++;
}
return sum;
}
int main() {
int ans=0;
int nx,ny;
n=mread(),m=mread();
for(int i=1; i<=n; i++)scanf("%s",mp[i]+1);
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
if(mp[i][j]=='L') {
for(int k=0; k<4; k++) {
nx=i+dx[k],ny=j+dy[k];
if(check(nx,ny) && mp[nx][ny]=='C')
mp[nx][ny]='W';
}
}
}
}
// for(int i=1;i<=n;i++)printf("%s\n",mp[i]+1);
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
if(mp[i][j]=='L' && !vis[i][j]) {
ans++;
dfs(i,j);
}
// printf("%d\n",ans);
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
if(mp[i][j]!='C')continue;
ans++;
if((i^j) & 1)lef.push_back((i-1)*m+j);
else rig.push_back((i-1)*m+j);
if(check(i,j+1) && mp[i][j+1]=='C')
addedge((i-1)*m+j,(i-1)*m+j+1);
if(check(i+1,j) && mp[i+1][j]=='C')
addedge((i-1)*m+j,i*m+j);
}
}
// for(auto x : lef)printf("%d ",x);
// printf("\n");
// for(auto x : rig)printf("%d ",x);
// printf("\n");
printf("%d\n",ans-hungary());
return 0;
}