LOJ2057 [TJOI2016 & HEOI2016 游戏]

传送门

题目大意

给定一个$n\le50$行$m\le50$列的网格图,每个格子可能是空地*、软石x或者硬石#

你可以且仅可以在空地上放炸弹,炸弹会以十字形方向爆炸。

炸弹可以炸穿软石,但是不能炸穿硬石。

询问最多能放几个炸弹。

解析

一开始看到$n,m\le50$的范围我以为是一个状压然后玄学的题。

后来发现其实就是个二分图。

我们考虑把每个空地能炸穿的范围全部弄出来,它的表现就是横竖方向的两个区间,我们把这里那个区间连边。

什么时候一个炸弹不会炸到另一个炸弹呢?就是一个横区间恰好对应一个纵区间的时候!(单身的没人权)

那这就是一个二分图匹配了。

Code

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
#define DREP(i, s, e) for(int i = s; i >= e ;i--)
#define REP(i, s, e) for(int i = s; i <= e ;i++)

#define DEBUG fprintf(stderr, "Passing [%s] in Line %d\n", __FUNCTION__, __LINE__)
#define chkmax(a, b) a = max(a, b)
#define chkmin(a, b) a = min(a, b)

#include <iostream>
#include <cstdio>

using namespace std;
const int maxn = 50 + 5, maxm = 50 + 5, maxN = maxn * maxm << 1, maxE = maxn * maxm, inf = 1e9;

int bg[maxN], to[maxE << 1], ne[maxE << 1], w[maxE << 1], e = 1;
inline void add(int x, int y, int z)
{
e++;
to[e] = y;
ne[e] = bg[x];
bg[x] = e;
w[e] = z;
}
inline void link(int x, int y) {add(x, y, 1);add(y, x, 0);}

template <typename T> inline T read()
{
T ans(0), p(1);
char c = getchar();
while (!isdigit(c))
{
if (c == '-') p = -1;
c = getchar();
}
while (isdigit(c))
{
ans = ans * 10 + c - 48;
c = getchar();
}
return ans * p;
}

char s[maxn][maxm];
int belongx[maxn][maxm], belongy[maxn][maxm];
int n, m, N, S, T, curx, cury, ans;

int cur[maxN], dis[maxN], q[maxN], head, tail;
inline bool bfs()
{
REP(i, 1, N) dis[i] = -1;
dis[q[head = tail = 1] = T] = 0;
while (head <= tail)
{
int x = q[head++];
for (int i = bg[x]; i ; i = ne[i])
if (w[i ^ 1] > 0 && dis[to[i]] == -1) dis[q[++tail] = to[i]] = dis[x] + 1;
}
return dis[S] != -1;
}
int dfs(int x = S, int y = inf)
{
if (x == T || !y) return y;
int res(0);
for (int &i = cur[x]; i ; i = ne[i])
if (w[i] > 0 && dis[to[i]] == dis[x] - 1)
{
int temp = dfs(to[i], min(y, w[i]));
if (temp > 0)
{
res += temp;
w[i] -= temp;
w[i ^ 1] += temp;
y -= temp;
if (!y) goto End;
}
}
End: return res;
}

inline void dinic()
{
while (bfs())
{
copy(bg + 1, bg + 1 + N, cur + 1);
ans += dfs();
}
}

int main()
{
#ifdef CraZYali
freopen("2057.in", "r", stdin);
freopen("2057.out", "w", stdout);
#endif
cin >> n >> m;
REP(i, 1, n) scanf("%s", s[i] + 1);
REP(i, 1, n)
REP(j, 1, m) if (s[i][j] != '#') belongx[i][j] = belongx[i][j-1] ? belongx[i][j-1] : ++curx;
REP(j, 1, m)
REP(i, 1, n) if (s[i][j] != '#') belongy[i][j] = belongy[i-1][j] ? belongy[i-1][j] : ++cury;
REP(i, 1, n)
REP(j, 1, m) if (s[i][j] == '*') link(belongx[i][j], curx + belongy[i][j]);
S = curx + cury + 1;
N = T = curx + cury + 2;
REP(i, 1, curx) link(S, i);
REP(i, 1, cury) link(i + curx, T);
dinic();
cout << ans << endl;
return 0;
}

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×