日常训练 20170602 Book

题意:小Z曾经是集邮部的成员,集邮部经常举办换邮票活动。活动中,如果两个人互相喜欢对方的邮票,那么这两个人就可以彼此交换自己的邮票。但在这个规则下,小Z没有换到自己喜欢的邮票。小Z觉得这是规则不完善导致的,于是小Z决定制定一个新的交换规则:每次可以选择任意多个人排成一个圆圈,如果每个人都喜欢他前边的人当前拥有的那枚邮票,就可以让每个人都拿走自己前边的人的邮票,并把自己的邮票给后边的人。在活动中可以进行任意多次这样的交换,并且一个人也可以多次参与这样的交换。
现在小 Z 知道了参加活动的人数,以及每个人喜欢哪些邮票,他想知道这次能不能让每个人都拿到一枚自己喜欢的邮票。你能帮他解决这个问题吗?
n 个人, m 组喜欢关系, 2n10000,0m20000 ,保证二元组 (x,y) 不重复。每个测试点数据不超过 10 组。

不知所措,猜了一个结论,只要每个点有入度出度就判 Yes ,结果被出题人发现我这样水过了数据,就加强了数据,把我卡成 30 分。题解是只要人与邮票能匹配就一定存在一种方案使得有解[完全不知道怎么证的][辣鸡猜结论][必要性解题]。结果就变成二分图最大匹配了。。。

#include<bits/stdc++.h>
const int N = 2e4 + 10;
const int M = 1e5 + 10;
const int INF = 1e9;
template <typename T> void read (T &x) {
    x = 0; char c = getchar();
    for (; !isdigit(c); c = getchar());
    for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
}
int n, m, S, T, x, y, s, first[N], h[N], q[N];
struct edge{
    int y, v, next;
}mp[M];
void ins(int x, int y) {
    mp[++s] = (edge) {y,1,first[x]}; first[x] = s;
    mp[++s] = (edge) {x,0,first[y]}; first[y] = s;
}
bool bfs() {
    memset(h, 0, sizeof(h));
    int head = 1, tail = 1;
    h[q[head] = S] = 1;
    for (int x=q[head]; head <= tail; x=q[++head])
        for (int t=first[x]; t; t=mp[t].next)
            if (mp[t].v && !h[mp[t].y])
                h[mp[t].y] = h[x] + 1,
                q[++tail] = mp[t].y;
    return h[T] > 0;
}
int dfs(int x, int f) {
    if (x == T) return f;
    int used = 0, b;
    for (int t=first[x]; t; t=mp[t].next)
        if (h[mp[t].y] == h[x] + 1) {
            b = dfs(mp[t].y, std::min(mp[t].v, f-used));
            mp[t].v -= b;
            mp[t^1].v += b;
            used += b;
            if (used == f) return used;
        }
    h[x] = -1;
    return used;
}
int main() {
    while (scanf("%d%d", &n, &m) != EOF) {
        memset(first, 0, sizeof(first)); s = 1;
        S = 0; T = n * 2 + 1;
        while (m--) read(x), read(y), ins(x, n + y);
        for (int i=1; i <= n; i++) ins(S, i), ins(n + i, T);
        while (bfs()) dfs(S, INF);
        bool legal = 1;
        for (int t=first[S]; t; t=mp[t].next)
            if (mp[t].v) {legal = 0; break;}
        if (legal)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值