Chaos Slover 补档计划 - 网络流相关题目与应用(2 最小割)

2015.11.25 19:55 Wed| 1 visits oi_2016| ChaosSlover补档计划| Text

二分图最小点权覆盖

从 x 或 y 集合中选取一些点,使得这些点覆盖了二分图中所有的边,并且使得选出来的点的权值和尽可能小。

建模:对于原图中边 (u,v) 在网络图中连结容量为 INF 的有向边 (u,v),s 和 x 集合中的点、y 和 t 集合中的点分别连边,容量为对应点的权值。最小点权覆盖即为该网络图中的最小割。

二分图最大点权独立集

在二分图中找到权值和最大的点集,使得它们两两之间没有边相连。答案就等于总点权和-最小点权覆盖。

HOJ2634 How to earn more

典型的最大权闭合子图裸题,和太空飞行计划问题一模一样。

HOJ2713 Matrix1

网格染色后得到了一个二分图,然后求的就是最大点权独立集。

  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
115
116
117
#include <bits/stdc++.h>
using namespace std;

#define N 2505
#define M 20000
#define inf 0x3f3f3f3f

int test, n, m, a[55][55], ans;
int s, t, head[N], cnt, d[N];

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

struct graph
{
    int next, to, val;
    graph() {}
    graph(int _next, int _to, int _val)
    : next(_next), to(_to), val(_val) {}
} edge[M];

inline void add(int x, int y, int z)
{
    edge[++cnt] = graph(head[x], y, z);
    head[x] = cnt;
    edge[++cnt] = graph(head[y], x, 0);
    head[y] = cnt;
}

queue<int> q;

bool bfs()
{
    memset(d, 0, sizeof d);
    while (!q.empty()) q.pop();
    d[s] = 1; q.push(s);
    while (!q.empty())
    {
        int x = q.front(); q.pop();
        for (int i = head[x]; i; i = edge[i].next)
            if (edge[i].val && !d[edge[i].to])
            {
                d[edge[i].to] = d[x] + 1;
                if (edge[i].to == t) return true;
                q.push(edge[i].to);
            }
    }
    return false;
}

int dfs(int x, int f)
{
    if (x == t) return f;
    int temp = f;
    for (int i = head[x]; i; i = edge[i].next)
        if (edge[i].val && d[edge[i].to] == d[x] + 1)
        {
            int k = dfs(edge[i].to, min(temp, edge[i].val));
            if (k == 0) d[edge[i].to] = 0;
            edge[i].val -= k; edge[i ^ 1].val += k;
            if (!(temp -= k)) break;
        }
    return f - temp;
}

int dinic()
{
    int re = 0;
    while (bfs())
        re += dfs(s, inf);
    return re;
}

void init()
{
    memset(head, 0, sizeof head); cnt = 1; ans = 0;
}

inline int pos(int x, int y)
{
    return m * (x - 1) + y;
}

int main()
{
    cin >> test;
    while (test--)
    {
        init();
        cin >> n >> m;
        s = 0; t = n * m + 1;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
                a[i][j] = read(), ans += a[i][j];
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)
            {
                if ((i + j) & 1)
                {
                    add(s, pos(i, j), a[i][j]);
                    if (i - 1 >= 1) add(pos(i, j), pos(i - 1, j), inf);
                    if (j - 1 >= 1) add(pos(i, j), pos(i, j - 1), inf);
                    if (i + 1 <= n) add(pos(i, j), pos(i + 1, j), inf);
                    if (j + 1 <= m) add(pos(i, j), pos(i, j + 1), inf);
                }
                else
                    add(pos(i, j), t, a[i][j]);
            }
        cout << ans - dinic() << endl;
    }
    return 0;
}

POJ1815 Friendship

求字典序最小的方案数,于是就只能枚举每一个点是否可以在答案中了,每一次需要重新建图。无解的情况是 s 和 t 直接相连。

  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
115
116
117
118
119
120
121
#include <bits/stdc++.h>
using namespace std;

#define N 405
#define M 200000
#define inf 0x3f3f3f3f

int test, n, m, f[N][N], ans[N];
int s, t, head[N], cnt, d[N];

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

struct graph
{
    int next, to, val;
    graph() {}
    graph(int _next, int _to, int _val)
    : next(_next), to(_to), val(_val) {}
} edge[M];

inline void add(int x, int y, int z)
{
    edge[++cnt] = graph(head[x], y, z);
    head[x] = cnt;
    edge[++cnt] = graph(head[y], x, 0);
    head[y] = cnt;
}

queue<int> q;

bool bfs()
{
    memset(d, 0, sizeof d);
    while (!q.empty()) q.pop();
    d[s] = 1; q.push(s);
    while (!q.empty())
    {
        int x = q.front(); q.pop();
        for (int i = head[x]; i; i = edge[i].next)
            if (edge[i].val && !d[edge[i].to])
            {
                d[edge[i].to] = d[x] + 1;
                if (edge[i].to == t) return true;
                q.push(edge[i].to);
            }
    }
    return false;
}

int dfs(int x, int f)
{
    if (x == t) return f;
    int temp = f;
    for (int i = head[x]; i; i = edge[i].next)
        if (edge[i].val && d[edge[i].to] == d[x] + 1)
        {
            int k = dfs(edge[i].to, min(temp, edge[i].val));
            if (k == 0) d[edge[i].to] = 0;
            edge[i].val -= k; edge[i ^ 1].val += k;
            if (!(temp -= k)) break;
        }
    return f - temp;
}

int dinic()
{
    int re = 0;
    while (bfs())
        re += dfs(s, inf);
    return re;
}

void init()
{
    memset(head, 0, sizeof head); cnt = 1;
}

inline int pos(int x, int y)
{
    return x * 2 + y;
}

int check()
{
    init();
    for (int i = 1; i <= n; ++i)
        add(pos(i, 0), pos(i, 1), 1);
    for (int i = 1; i <= n; ++i)
        if (!ans[i])
            for (int j = 1; j <= n; ++j)
                if (j != i && f[i][j])
                    add(pos(i, 1), pos(j, 0), inf);
    return dinic();
}

int main()
{
    cin >> n >> s >> t;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
            f[i][j] = read();
    if (f[s][t]) { puts("NO ANSWER!"); return 0; }
    s = pos(s, 1); t = pos(t, 0);
    int cut = check();
    printf("%d\n", cut);
    for (int temp, i = 1; i <= n; ++i)
    {
        if (pos(i, 1) == s || pos(i, 0) == t) continue;
        ans[i] = 1; temp = check();
        temp < cut ? cut = temp : ans[i] = 0;
    }
    for (int i = 1; i <= n; ++i)
        if (ans[i]) printf("%d ", i);
    return 0;
}

ZOJ2532 Internship

最小割的必须边就是两端点能够分别和源点和汇点连通的边。两遍简单的 DFS 之后枚举一遍就可以解决。

  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
115
116
117
118
119
120
121
122
123
#include <bits/stdc++.h>
using namespace std;

#define N 150
#define M 5000
#define inf 0x3f3f3f3f

int n, m, l, x[1005], y[1005], vis[N], vis2[N];
int s, t, head[N], cnt, d[N];

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

struct graph
{
    int next, to, val;
    graph() {}
    graph(int _next, int _to, int _val)
    : next(_next), to(_to), val(_val) {}
} edge[M];

inline void add(int x, int y, int z)
{
    edge[++cnt] = graph(head[x], y, z);
    head[x] = cnt;
    edge[++cnt] = graph(head[y], x, 0);
    head[y] = cnt;
}

queue<int> q;

bool bfs()
{
    memset(d, 0, sizeof d);
    while (!q.empty()) q.pop();
    d[s] = 1; q.push(s);
    while (!q.empty())
    {
        int x = q.front(); q.pop();
        for (int i = head[x]; i; i = edge[i].next)
            if (edge[i].val && !d[edge[i].to])
            {
                d[edge[i].to] = d[x] + 1;
                if (edge[i].to == t) return true;
                q.push(edge[i].to);
            }
    }
    return false;
}

int dfs(int x, int f)
{
    if (x == t) return f;
    int temp = f;
    for (int i = head[x]; i; i = edge[i].next)
        if (edge[i].val && d[edge[i].to] == d[x] + 1)
        {
            int k = dfs(edge[i].to, min(temp, edge[i].val));
            if (!k) d[edge[i].to] = 0;
            edge[i].val -= k; edge[i ^ 1].val += k;
            if (!(temp -= k)) break;
        }
    return f - temp;
}

int dinic()
{
    int re = 0;
    while (bfs())
        re += dfs(s, inf);
    return re;
}

void init()
{
    memset(head, 0, sizeof head); cnt = 1;
    memset(vis, 0, sizeof vis);
    memset(vis2, 0, sizeof vis2);
}

void find(int x)
{
    vis[x] = 1;
    for (int i = head[x]; i; i = edge[i].next)
        if (edge[i].val && !vis[edge[i].to])
            find(edge[i].to);
}

void find2(int x)
{
    vis2[x] = 1;
    for (int i = head[x]; i; i = edge[i].next)
        if (edge[i ^ 1].val && !vis2[edge[i].to])
            find2(edge[i].to);
}

int main()
{
    while (cin >> n >> m >> l, n)
    {
        init();
        s = n + m + 1; t = 0;
        for (int i = 1; i <= n; ++i)
            add(s, i, inf);
        for (int z, i = 1; i <= l; ++i)
            x[i] = read(), y[i] = read(), z = read(), add(x[i], y[i], z);
        dinic(); find(s); find2(t);
        int flag = 0;
        for (int i = 1; i <= l; ++i)
            if (vis[x[i]] && vis2[y[i]])
            {
                if (flag) printf(" ");
                printf("%d", i), flag = 1;
            }
        puts("");
    }
    return 0;
}

Ural1277 Cops and Thieves

怎样来看都是十分经典的构图方式啊。这样的题今天已经做了好多了……

 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
#include <bits/stdc++.h>
using namespace std;

#define N 205
#define M 50000
#define inf 0x3f3f3f3f

int k, n, m;
int s, t, head[N], d[N];

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

struct graph
{
    int next, to, val;
    graph() {}
    graph(int _next, int _to, int _val)
    : next(_next), to(_to), val(_val) {}
} edge[M];

inline void add(int x, int y, int z)
{
    static int cnt = 1;
    edge[++cnt] = graph(head[x], y, z);
    head[x] = cnt;
    edge[++cnt] = graph(head[y], x, 0);
    head[y] = cnt;
}

queue<int> q;

bool bfs()
{
    memset(d, 0, sizeof d);
    while (!q.empty()) q.pop();
    d[s] = 1; q.push(s);
    while (!q.empty())
    {
        int x = q.front(); q.pop();
        for (int i = head[x]; i; i = edge[i].next)
            if (edge[i].val && !d[edge[i].to])
            {
                d[edge[i].to] = d[x] + 1;
                if (edge[i].to == t) return true;
                q.push(edge[i].to);
            }
    }
    return false;
}

int dfs(int x, int f)
{
    if (x == t) return f;
    int temp = f;
    for (int i = head[x]; i; i = edge[i].next)
        if (edge[i].val && d[edge[i].to] == d[x] + 1)
        {
            int k = dfs(edge[i].to, min(temp, edge[i].val));
            if (k == 0) d[edge[i].to] = 0;
            edge[i].val -= k; edge[i ^ 1].val += k;
            if (!(temp -= k)) break;
        }
    return f - temp;
}

int dinic()
{
    int re = 0;
    while (bfs())
        re += dfs(s, inf);
    return re;
}

inline int pos(int x, int y)
{
    return x * 2 + y;
}

int main()
{
    cin >> k >> n >> m >> s >> t;
    if (s == t) { puts("NO"); return 0; }
    s = pos(s, 1); t = pos(t, 0);
    for (int i = 1; i <= n; ++i)
        add(pos(i, 0), pos(i, 1), read());
    for (int x, y, i = 1; i <= m; ++i)
        x = read(), y = read(),
        add(pos(x, 1), pos(y, 0), inf), add(pos(y, 1), pos(x, 0), inf);
    if (dinic() <= k) puts("YES");
    else puts("NO");
    return 0;
}

SPOJOPTM Optimal Marks

把异或操作拆成一位一位操作,这样每一位就只能有 0 和 1 两个取值了。考虑让取值为 0 和取值为 1 的点分别在割出来的两个集合中。对于一条边,如果两边的点取值不同,就会产生价值,而最小割能够保证产生的价值最小。

  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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <bits/stdc++.h>
using namespace std;

#define N 550
#define M 20000
#define inf 0x3f3f3f3f

int test, n, m, k, num[N], mark[N], vis[N], x[M], y[M];
int s, t, head[N], cnt, d[N];

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

struct graph
{
    int next, to, val;
    graph() {}
    graph(int _next, int _to, int _val)
    : next(_next), to(_to), val(_val) {}
} edge[M];

inline void add(int x, int y, int z)
{
    edge[++cnt] = graph(head[x], y, z);
    head[x] = cnt;
    edge[++cnt] = graph(head[y], x, 0);
    head[y] = cnt;
}

queue<int> q;

bool bfs()
{
    memset(d, 0, sizeof d);
    while (!q.empty()) q.pop();
    d[s] = 1; q.push(s);
    while (!q.empty())
    {
        int x = q.front(); q.pop();
        for (int i = head[x]; i; i = edge[i].next)
            if (edge[i].val && !d[edge[i].to])
            {
                d[edge[i].to] = d[x] + 1;
                if (edge[i].to == t) return true;
                q.push(edge[i].to);
            }
    }
    return false;
}

int dfs(int x, int f)
{
    if (x == t) return f;
    int temp = f;
    for (int i = head[x]; i; i = edge[i].next)
        if (edge[i].val && d[edge[i].to] == d[x] + 1)
        {
            int k = dfs(edge[i].to, min(temp, edge[i].val));
            if (!k) d[edge[i].to] = 0;
            edge[i].val -= k; edge[i ^ 1].val += k;
            if (!(temp -= k)) break;
        }
    return f - temp;
}

int dinic()
{
    int re = 0;
    while (bfs())
        re += dfs(s, inf);
    return re;
}

void init()
{
    memset(num, 0, sizeof num);
    memset(mark, 0, sizeof mark);
}

void dfs2(int x, int base)
{
    vis[x] = 1;
    if ((num[x] & base) == 0) num[x] += base;
    for (int i = head[x]; i; i = edge[i].next)
        if (edge[i].val && !vis[edge[i].to])
            dfs2(edge[i].to, base);
}

void getans(int base)
{
    memset(head, 0, sizeof head); cnt = 1;
    memset(vis, 0, sizeof vis);
    s = 0; t = n + 1;
    for (int i = 1; i <= n; ++i)
        if (mark[i])
        {
            if (num[i] & base) add(s, i, inf);
            else add(i, t, inf);
        }
    for (int i = 1; i <= m; ++i)
        add(x[i], y[i], 1), add(y[i], x[i], 1);
    dinic();
    dfs2(s, base);
}

int main()
{
    cin >> test;
    while (test--)
    {
        init();
        cin >> n >> m;
        for (int i = 1; i <= m; ++i)
            x[i] = read(), y[i] = read();
        cin >> k;
        for (int x, i = 1; i <= k; ++i)
            x = read(), num[x] = read(), mark[x] = true;
        for (int i = 0; i <= 30; ++i)
            getans(1 << i);
        for (int i = 1; i <= n; ++i)
            printf("%d\n", num[i]);
    }
    return 0;
}

SPOJCOCONUTS Coconuts

和上一道题的模型又是基本相同的……

 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
#include <bits/stdc++.h>
using namespace std;

#define N 305
#define M 200005
#define inf 0x3f3f3f3f

int n, m;
int s, t, head[N], cnt, d[N];

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

struct graph
{
    int next, to, val;
    graph() {}
    graph(int _next, int _to, int _val)
    : next(_next), to(_to), val(_val) {}
} edge[M];

inline void add(int x, int y, int z)
{
    edge[++cnt] = graph(head[x], y, z);
    head[x] = cnt;
    edge[++cnt] = graph(head[y], x, 0);
    head[y] = cnt;
}

queue<int> q;

bool bfs()
{
    memset(d, 0, sizeof d);
    while (!q.empty()) q.pop();
    d[s] = 1; q.push(s);
    while (!q.empty())
    {
        int x = q.front(); q.pop();
        for (int i = head[x]; i; i = edge[i].next)
            if (edge[i].val && !d[edge[i].to])
            {
                d[edge[i].to] = d[x] + 1;
                if (edge[i].to == t) return true;
                q.push(edge[i].to);
            }
    }
    return false;
}

int dfs(int x, int f)
{
    if (x == t) return f;
    int temp = f;
    for (int i = head[x]; i; i = edge[i].next)
        if (edge[i].val && d[edge[i].to] == d[x] + 1)
        {
            int k = dfs(edge[i].to, min(temp, edge[i].val));
            if (!k) d[edge[i].to] = 0;
            edge[i].val -= k; edge[i ^ 1].val += k;
            if (!(temp -= k)) break;
        }
    return f - temp;
}

int dinic()
{
    int re = 0;
    while (bfs())
        re += dfs(s, inf);
    return re;
}

void init()
{
    memset(head, 0, sizeof head); cnt = 1;
}

int main()
{
    while (cin >> n >> m, n)
    {
        init();
        s = 0; t = n + 1;
        for (int i = 1; i <= n; ++i)
            if (read()) add(s, i, 1);
            else add(i, t, 1);
        for (int x, y, i = 1; i <= m; ++i)
            x = read(), y = read(),
            add(x, y, 1), add(y, x, 1);
        cout << dinic() << endl;
    }
    return 0;
}

POJ3921 Destroying the bus stations

找出所有可能需要删的点,拆点后在两个点之间连一条权值为 1 的边,然后求最小割。

另一种建图方式:找到所有可能需要删的边,权值设为 inf。

  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
#include <bits/stdc++.h>
using namespace std;

#define N 105
#define M 20005
#define inf 0x3f3f3f3f

int n, m, k, x[M], y[M], dist[N][N];
int s, t, head[N], cnt, d[N];

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

struct graph
{
    int next, to, val;
    graph() {}
    graph(int _next, int _to, int _val)
    : next(_next), to(_to), val(_val) {}
} edge[M];

inline void add(int x, int y, int z)
{
    edge[++cnt] = graph(head[x], y, z);
    head[x] = cnt;
    edge[++cnt] = graph(head[y], x, 0);
    head[y] = cnt;
}

queue<int> q;

bool bfs()
{
    memset(d, 0, sizeof d);
    while (!q.empty()) q.pop();
    d[s] = 1; q.push(s);
    while (!q.empty())
    {
        int x = q.front(); q.pop();
        for (int i = head[x]; i; i = edge[i].next)
            if (edge[i].val && !d[edge[i].to])
            {
                d[edge[i].to] = d[x] + 1;
                if (edge[i].to == t) return true;
                q.push(edge[i].to);
            }
    }
    return false;
}

int dfs(int x, int f)
{
    if (x == t) return f;
    int temp = f;
    for (int i = head[x]; i; i = edge[i].next)
        if (edge[i].val && d[edge[i].to] == d[x] + 1)
        {
            int k = dfs(edge[i].to, min(temp, edge[i].val));
            if (!k) d[edge[i].to] = 0;
            edge[i].val -= k; edge[i ^ 1].val += k;
            if (!(temp -= k)) break;
        }
    return f - temp;
}

int dinic()
{
    int re = 0;
    while (bfs())
        re += dfs(s, inf);
    return re;
}

void init()
{
    memset(head, 0, sizeof head); cnt = 1;
    memset(dist, 0x3f, sizeof dist);
}

int main()
{
    while (cin >> n >> m >> k, n)
    {
        init();
        s = n + 1; t = n;
        for (int i = 1; i <= m; ++i)
            x[i] = read(), y[i] = read(), dist[x[i]][y[i]] = 1;
        for (int i = 1; i <= n; ++i)
            dist[i][i] = 0;
        for (int t = 1; t <= n; ++t)
            for (int i = 1; i <= n; ++i)
                for (int j = 1; j <= n; ++j)
                    dist[i][j] = min(dist[i][j], dist[i][t] + dist[t][j]);
        for (int i = 2; i < n; ++i)
            if (dist[1][i] + dist[i][n] <= k) add(i, n + i, 1);
        for (int i = 1; i <= m; ++i)
            add(x[i] + n, y[i], inf);
        /*
        for (int i = 2; i < n; ++i) add(i, n + i, 1);
        for (int i = 1; i <= m; ++i)
            if (dist[1][x[i]] + dist[y[i]][n] < k)
                add(x[i] + n, y[i], inf);
        */
        cout << dinic() << endl;
    }
    return 0;
}