题解 P2711 【小行星】
看到数据范围,以及“消除”、“最少次数”的字样,就知道与最小割有关。问题在于怎么割呢?
要消除一颗小行星,可以从三个面来消除。
我们想要消除所有的小行星,就说明每一颗小行星在
于是我们对于每一颗小行星,从
建出来的图大致如下(样例):
根据上面的分析,显然地,就是求
但是……图好乱……
不难发现,这张图是可以简化的。注意到从超级源
于是我们简化了这张图,改为直接从超级源
简化后的图大致如下:
这样只要拆
最后求解该图的最小割即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int MAXN = 500, INF = 0x7F7F7F7F;
struct edge {
int to, cap, rev;
};
int n, k;
vector <edge> G[MAXN*4+2];
edge make_edge(int to, int cap, int rev) {
edge x;
x.to = to, x.cap = cap, x.rev = rev;
return x;
}
void add_edge(int from, int to, int cap) {
G[from].push_back( make_edge(to, cap, G[to].size()) );
G[to].push_back( make_edge(from, 0, G[from].size() - 1) );
}
void init() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
add_edge(x, y + 500, INF);
add_edge(y + 500, y + 1000, 1);
add_edge(y + 1000, z + 1500, INF);
}
for (int i = 1; i <= 500; ++i) {
add_edge(0, i, 1);
add_edge(i + 1500, 2001, 1);
}
}
namespace Dinic {
int level[MAXN*4+2];
int iter[MAXN*4+2];
void bfs(int s) {
bool mark[MAXN*4+2];
queue <int> q;
memset(mark, 0, sizeof mark);
memset(level, -1, sizeof level);
q.push(s);
mark[s] = true;
level[s] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
mark[u] = false;
for (int i = 0; i < G[u].size(); ++i) {
edge &e = G[u][i];
if (e.cap > 0 && level[e.to] < 0) {
level[e.to] = level[u] + 1;
if (!mark[e.to]) {
mark[e.to] = true;
q.push(e.to);
}
}
}
}
}
int dfs(int u, int t, int f) {
if (u == t)
return f;
for (int &i = iter[u]; i < G[u].size(); ++i) {
edge &e = G[u][i];
if (e.cap > 0 && level[e.to] > level[u]) {
int d = dfs(e.to, t, min(f, e.cap));
if (d > 0) {
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
int max_flow(int s, int t) {
int flow = 0;
for (;;) {
bfs(s);
if (level[t] < 0)
return flow;
memset(iter, 0, sizeof iter);
int f;
while ( (f = dfs(s, t, INF)) > 0 )
flow += f;
}
}
}
int main() {
init();
printf("%d\n", Dinic::max_flow(0, 2001));
return 0;
}