Description
滑雪场坐落在 FJ 省西北部的若干座山上.
从空中鸟瞰, 滑雪场可以看作一个有向无环图, 每条弧代表一个斜坡 (即雪道), 弧的方向 代表斜坡下降的方向.
你的团队负责每周定时清理雪道. 你们拥有一架直升飞机, 每次飞行可以从总部带一个人降落到滑雪场的某个地点, 然后再飞回总部. 从降落的地点出发, 这个人可以顺着斜坡向下滑行, 并清理他所经过的雪道.
由于每次飞行的耗费是固定的, 为了最小化耗费, 你想知道如何用最少的飞行次数才能完成 清理雪道的任务.
Input
输入文件的第一行包含一个整数 \(n (2 \leq n \leq 100)\)----代表滑雪场的地点的数量.
接下来的 \(n\) 行, 描述 \(1 \sim n\) 号地点出发的斜坡, 第 \(i\) 行的第一个数为 \(m_i (0 \leq m_i \lt n)\), 后面共有 \(m_i\) 个整数, 由空格隔开, 每个整数 \(a_{i,j}\) 互不相同, 代表从地点 \(i\) 下降到地点 \(a_{i,j}\) 的斜坡. 每个地点至少有一个斜坡与之相连.
Output
输出文件的第一行是一个整数 \(k\)----直升飞机的最少飞行次数.
Sample Input
8
1 3
1 7
2 4 5
1 8
1 8
0
2 6 5
0
Sample Output
4
Explanation
每条雪道拆成俩个结点
就是通过上下界限制每条雪道至少通过 1 次
可能我的做法不是很正常, 因为 @Burst_Zero 大神告诉我其实这道题用最小流两个方向 各跑一遍就可以出来了~
话说我为什么调了两天呢...... 因为我每一次跑最大流的时候都会把总流量设成 0, 然后从头 跑一遍一点意思都没有, 所以就不停地死循环加边~ 突然脑洞一开发现了这个 bug
Source Code
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
typedef int lli;
const int maxn = 30010, maxm = 500100;
// const lli infinit = 0x007f7f7f7f7f7f7fll;
const int infinit = 0x7fffffff;
class Dinic
{
public:
struct edge
{
int u, v;
lli flow;
edge *next, *rev;
};
int n, s, t, ecnt;
edge *edges[maxn], epool[maxm], *edges_bak[maxn];
int level[maxn];
void add_edge(int u, int v, lli flow)
{
edge *p = &epool[++ecnt],
*q = &epool[++ecnt];
p->u = u; p->v = v; p->flow = flow;
p->next = edges[u]; edges[u] = p; p->rev = q;
q->u = v; q->v = u; q->flow = 0;
q->next = edges[v]; edges[v] = q; q->rev = p;
return ;
}
bool make_level(void)
{
memset(level, 0, sizeof(level));
queue<int> que;
que.push(s);
level[s] = 1;
while (!que.empty()) {
int p = que.front();
que.pop();
for (edge *ep = edges[p]; ep; ep = ep->next)
if (ep->flow && !level[ep->v]) {
level[ep->v] = level[p] + 1;
que.push(ep->v);
}
}
if (level[t] > 0)
return true;
return false;
}
lli find(int p, lli mn)
{
if (p == t)
return mn;
lli sum = 0;
for (edge *ep = edges[p]; ep && sum < mn; ep = ep->next)
if (ep->flow && level[ep->v] == level[p] + 1) {
lli tmp = find(ep->v, min(mn - sum, ep->flow));
if (tmp > 0) {
ep->flow -= tmp;
ep->rev->flow += tmp;
sum += tmp;
}
}
if (sum <= 0)
level[p] = 0;
return sum;
}
lli max_flow;
lli eval(void)
{
// Backing up
for (int i = 0; i <= n; i++)
edges_bak[i] = edges[i];
// Evaluating
while (make_level()) {
// Restoring stuff
for (int i = 0; i <= n; i++)
edges[i] = edges_bak[i];
// Deep first searching, literally.
lli tmp = find(s, infinit);
if (tmp <= 0)
break;
max_flow += tmp;
}
return max_flow;
}
} graph;
int n, m;
int degree[maxn];
int main(int argc, char** argv)
{
scanf("%d", &n);
for (int i = 1, x; i <= n; i++) {
scanf("%d", &x); // How many linkages
for (int j = 1, y; j <= x; j++) {
scanf("%d", &y); // Target
m += 1;
graph.add_edge(i, n + 2*m - 1, infinit);
graph.add_edge(n + 2*m, y, infinit);
}
}
graph.n = n + 2*m + 2;//3;
int S = n + 2*m + 1; // Source
graph.s = 0;//2; // Super-source
graph.t = n + 2*m + 2;//3; // Sink
// Adding connections in edges
for (int i = 1; i <= m; i++) {
graph.add_edge(n + 2*i - 1, n + 2*i, infinit);
degree[n + 2*i - 1] -= 1;
degree[n + 2*i] += 1;
}
// Begin, from source to arbitrary hilltop
for (int i = 1; i <= n; i++)
graph.add_edge(S, i, infinit);
// Limiting flow with degrees
for (int i = n+1; i <= n + 2*m; i++) {
if (degree[i] > 0)
graph.add_edge(graph.s, i, degree[i]);
else
graph.add_edge(i, graph.t, -degree[i]);
}
// Testing and flowing
for (int i = 1; i >= 1; i++) {
// break;
graph.add_edge(graph.s, S, 1);
lli res = graph.eval();
if (res >= m) {
printf("%d\n", i);
break;
}
}
return 0;
}