P2172题解

  1. 1. P2172 [国家集训队]部落战争 题解
    1. 1.1. 题目大意
    2. 1.2. 建模
    3. 1.3. 代码

P2172 [国家集训队]部落战争 题解

题目大意

给一张地图,其中有能走的点和不能走的点,并且给你 ,表示行走路线(比如象棋中的马就是 )。每个能走的点只能走一次,且只能向下走。可以从任一点开始,任一点结束,求出走完所有能走的点的最少开始次数。

建模

把题意化简完就会发现,这道题就是最小路径覆盖,这里简单说一下最短路径覆盖的思想:

既然要求最短路径覆盖,那么我们吧覆盖表示一下,覆盖=点总数-最大独立集,最大独立集其实就是最小割,因此覆盖=点总数-最小割,又因为最小割就是最大流,所以覆盖=点总数-最大流

问题转换完了,模型也就出来了。利用拆点的思想,将一个点拆为入点和出点,以限制一个点的经过次数。源点连向所有入点,所有出点连向汇点,入点连向对应出点,流量都为一。对于可以联通的两点 ,将 的入点连向 的出点,流量为一。最后跑最大流即可。

注意:

  • 由于只能往下走,所以不要在连边的时候连向上的边,一个点最多需要四次行走。

代码

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
#include <bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int N = 3e4 + 10;
const int M = 3e5 + 10;
int cnt = 1;
int head[N];
int n, m;
int s, t;
int now[N];
int dep[N];
struct edge {
int u, v, c, val, nxt;
edge(int u = 0, int v = 0, int c = 0, int val = 0, int nxt = 0): u(u), v(v), c(c), val(val), nxt(nxt) {}
} e[M];
void ADD(int u, int v, int c, int val) {
cnt++;
e[cnt] = edge(u, v, c, val, head[u]);
head[u] = cnt;
}
void add_edge(int u, int v, int c, int val) {
ADD(u, v, c, val);
ADD(v, u, 0, -val);
}
bool bfs() {
queue<int>q;
memset(dep, 0, sizeof dep);
memcpy(now, head, sizeof head);
dep[s] = 1;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
int c=e[i].c;
if(!c||dep[v]!=0) continue;
dep[v]=dep[u]+1;
q.push(v);
}
}
return dep[t]!=0;
}
int dfs(int u,int t,int flow){
if(u==t) return flow;
int nowflow=0;
for(int i=now[u];i&&nowflow<flow;i=e[i].nxt){
now[u]=i;
int v=e[i].v;
int c=e[i].c;
if(!c||dep[v]!=dep[u]+1)continue;
int ff=dfs(v,t,min(flow-nowflow,c));
if(ff) nowflow+=ff,e[i].c-=ff,e[i^1].c+=ff;
}
return nowflow;
}
int maxflow(){
int ans=0;
while(bfs()){
int nowflow;
while((nowflow=dfs(s,t,inf))) ans+=nowflow;
}
return ans;
}
int r, c;
int dx[4];
int dy[4];
int mp[55][55];
int tot;
int main()
{
cin>>n>>m>>r>>c;
int sum=0;
s=n*m*2+1,t=s+1;
dx[0]=r,dx[1]=c,dx[2]=r,dx[3]=c;
dy[0]=-c,dy[1]=-r,dy[2]=c,dy[3]=r;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
char cc;
cin>>cc;
if(cc=='.'){
mp[i][j]=++tot;
sum++;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(!mp[i][j])continue;
add_edge(s,mp[i][j],1,0);
add_edge(mp[i][j]+n*m,t,1,0);
for(int k=0;k<4;k++){
int x=i+dx[k],y=j+dy[k];
if(x<1||y<1||x>n||y>m||mp[x][y]==0)continue;
add_edge(mp[i][j],mp[x][y]+n*m,1,0);
}
}
}
cout<<sum-maxflow();
}