bzoj1143: [CTSC2008]祭祀river(二分图匹配)

7 篇文章 0 订阅
4 篇文章 0 订阅

题目传送门
题面有点坑(对于我这种不读题面的人来说)
题面上有个大大的图。我看了一下好像是环诶。
我就以为这题有环。
一开始
我想:
既然有环,那么我用强联通缩点之后每个环最多只能选出一个点咯。
然后我再在剩下的点里面求点集,使两两不能互相到达。

然后我看错了:
题目说显然,水系中不会有环流(下图描述一个环流的例子)
微笑。

然而这道题是裸的最大独立集。
学(mo)了一发。
师兄blog
最大独立集=点数-最大匹配。

代码实现:(有强联通版本。。)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
struct node {
    int x,y,next;
}a[1100];int len,last[110];
void ins(int x,int y) {
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
int belong[110],sta[1100],tp,cnt;
int low[110],dfn[110],id;
bool v[110];
void dfs(int x) {
    low[x]=dfn[x]=++id;
    sta[++tp]=x;v[x]=true;
    for(int k=last[x];k;k=a[k].next) {
        int y=a[k].y;
        if(dfn[y]==-1) {
            dfs(y);
            low[x]=min(low[x],low[y]);
        }
        else 
            if(v[y]==true)
                low[x]=min(low[x],dfn[y]);
    }
    if(dfn[x]==low[x]) {
        int i;cnt++;
        do {
            i=sta[tp--];
            belong[i]=cnt;
            v[i]=false;
        }while(i!=x);
    }
}
int map[110][110],match[110];
bool chw[110];
bool get_match(int x) {
    for(int i=1;i<=cnt;i++)
        if(map[x][i]==true)
            if(chw[i]==false) {
                chw[i]=true;
                if(match[i]==0||get_match(match[i])==true) {
                    match[i]=x;return true;
                }
            }
    return false;
}
int main() {
    int n,m;scanf("%d%d",&n,&m);
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<=m;i++) {
        int x,y;scanf("%d%d",&x,&y);
        ins(x,y);
    }
    memset(dfn,-1,sizeof(dfn));
    memset(sta,0,sizeof(sta));
    memset(v,false,sizeof(v));
    memset(low,0,sizeof(low));
    tp=id=cnt=0;
    for(int i=1;i<=n;i++)
        if(dfn[i]==-1)
            dfs(i);
    memset(map,false,sizeof(map));
    for(int i=1;i<=len;i++) 
        if(belong[a[i].x]!=belong[a[i].y])
            map[belong[a[i].x]][belong[a[i].y]]=true;
    for(int k=1;k<=cnt;k++)
        for(int i=1;i<=cnt;i++)
            for(int j=1;j<=cnt;j++)
                if(map[i][k]==true&&map[k][j]==true)
                    map[i][j]=true; //要加floyd使得间接连通也算联通
    int ans=0;memset(match,0,sizeof(match));
    for(int i=1;i<=cnt;i++) {
        memset(chw,false,sizeof(chw));
        if(get_match(i)==true)
            ans++;
    }
    printf("%d\n",cnt-ans);
    return 0;
}

不加强联通:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int map[110][110],match[110];
bool chw[110];int n;
bool get_match(int x) {
    for(int i=1;i<=n;i++)
        if(map[x][i]==true)
            if(chw[i]==false) {
                chw[i]=true;
                if(match[i]==0||get_match(match[i])==true) {
                    match[i]=x;return true;
                }
            }
    return false;
}
int main() {
    int m;scanf("%d%d",&n,&m);
    memset(map,false,sizeof(map));
    for(int i=1;i<=m;i++) {
        int x,y;scanf("%d%d",&x,&y);
        map[x][y]=true;
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(map[i][k]==true&&map[k][j]==true)
                    map[i][j]=true;
    int ans=0;
    memset(match,0,sizeof(match));
    for(int i=1;i<=n;i++) {
        memset(chw,false,sizeof(chw));
        if(get_match(i)==true)
            ans++;
    }
    printf("%d\n",n-ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值