【JZOJ4963】【GDKOI2017模拟1.21】Book

17 篇文章 0 订阅
9 篇文章 0 订阅

Description

曾经有一枚珍稀的邮票摆在我的面前,
我没有好好珍惜 等到失去时 才感到后悔。
——小Z
小Z曾经是集邮部的成员,集邮部经常举办换邮票活动。活动中,如果两个人互相喜欢对方的邮票,那么这两个人就可以彼此交换自己的邮票。但在这个规则下,小Z没有换到自己喜欢的邮票。小Z觉得这是规则不完善导致的,于是小Z决定制定一个新的交换规则:每次可以选择任意多个人排成一个圆圈,如果每个人都喜欢他前边的人当前拥有的那枚邮票,就可以让每个人都拿走自己前边的人的邮票,并把自己的邮票给后边的人。在活动中可以进行任意多次这样的交换,并且一个人也可以多次参与这样的交换。
现在小Z知道了参加活动的人数,以及每个人喜欢哪些邮票,他想知道这次能不能让每个人都拿到一枚自己喜欢的邮票。你能帮他解决这个问题吗?

Data Constraint

对于30%的数据,n,m≤3。
对于70%的数据,n,m≤30。
对于100%的数据,2≤n≤10000,0≤m≤20000。保证二元组(x,y)不重复。每个测试点数据不超过10组。

Solution

这道题要注意一个问题:每个人只对某个人手上的邮票有兴趣,而不是对那个人!换言之,我喜欢的不是你这个人,而是你的的邮票!!!
所以我们可以将人和邮票拆开,当x喜欢y的邮票时,我们就x向y的邮票连一条容量为1边,跑一边最大匹配,若最大匹配为n,则说明可行。至于为什么是正确的呢?我们可以这样想: 显然,感性证明一下即可。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=80005;
int first[maxn],last[maxn],next[maxn],value[maxn],dui[maxn],deep[maxn],v[maxn];
int n,i,t,j,k,l,x,y,p,m,num,bz1,a,b,ans;
void lian(int x,int y,int z){
    last[++num]=y;next[num]=first[x];first[x]=num;value[num]=z;
}
int bfs(){
    int i=0,j=1,t,k,l,x;v[1]=0;j=1;i=0;
    memset(deep,0,sizeof(deep));
    while (i<j){
        x=v[++i];
        for (t=first[x];t;t=next[t]){
            if (deep[last[t]] || !value[t] || !last[t])continue;
            v[++j]=last[t];deep[v[j]]=deep[x]+1;    
        }
    }
    if (!deep[n*2+1]) return 0;
    return 1;
}
int dg(int x,int sum){
    int t,k,l=sum;
    if (x==n*2+1) return sum;
    for (t=first[x];t;t=next[t]){
        if (!value[t] || deep[last[t]]!=deep[x]+1) continue;
        k=dg(last[t],value[t]);
        if (k){
            value[t]--;value[dui[t]]++;
            l--;
            if (!l) return sum;
        }
    }
    return sum-l;
}
int main(){
    freopen("book.in","r",stdin);freopen("book.out","w",stdout);
    while (scanf("%d",&n)!=EOF){
        scanf("%d",&m);num=0;
        memset(first,0,sizeof(first));
        for (i=1;i<=m;i++)
            scanf("%d%d",&x,&y),lian(x,y+n,1),lian(y+n,x,0);
        for (i=1;i<=n;i++)
            lian(0,i,1),lian(i,0,0);
        for (i=1;i<=n;i++)
            lian(i+n,n*2+1,1),lian(n*2+1,i+n,0);
        for (i=1;i<=num;i++)
            if (i%2) dui[i]=i+1;
            else dui[i]=i-1;
        ans=0;
        while (bfs()) ans+=dg(0,maxn);
        if (ans==n) printf("YES\n");
        else printf("NO\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值