POJ 3041 Asteroids

来源:互联网 发布:ai软件怎么使用 编辑:程序博客网 时间:2024/06/08 11:01

次元传送门


题目大意:
有一个n*n的棋盘,给出一些点的位置,询问最少去掉多少行和列,可以把所有点去掉,输出行列总数


分析:
这道题,棋盘,点,行,列,有没有想到什么??
没错
二分图………….
最大匹配………..
很惭愧(⊙﹏⊙)b,自己太弱了,对二分图理解不够深刻,没看出来这是个二分图>_<,在YOUSIKI童鞋的友情提示下才知道此题要用二分图解(ˇˍˇ) …………….
所以接下来就要说一说怎么求二分图最大匹配
锵锵锵锵!!!(^o^)/~匈牙利算法闪亮登场
先来说一说匈牙利算法的最核心思想吧—–找增广路
(⊙o⊙)…额,什么是增广路?_?
先要说一下什么叫做交替路:
在已有匹配P的二分图中,存在路径S,v1、v2、v3、v4……∈S,v1∈P,v2∉P,v3∈P,v4∉P……,那么S就是一条交替路
所谓增广路,就是起点和终点都∉P的一条交替路
下图中,9->4->8->1->6->2就是一条增广路
这里写图片描述
增广路一定是奇数条边,因为除了起点和终点其他点都是匹配点,一定存在奇数条边(偶数条匹配边,这是显然的,奇数条未匹配边),再加上由起点和终点衍生出来的两条未匹配边,一定是奇数条边……..
仔细观察,我们会发现,增广路中未匹配路径比匹配路径要多一条,这是很显然的……………因为刚刚我们说明了,除了起点和终点其他点都是匹配点,一定存在奇数条边(偶数条匹配边,这是显然的,奇数条未匹配边),并且未匹配边的个数比匹配边的个数少一,再加上由起点和终点衍生出来的两条未匹配边,所以未匹配边的个数比匹配边的个数多一…………..
有了以上性质,我们就可以计算最大匹配了
首先说有向边:
每一次从一个点出发,寻找增广路,如果找到了增广路,证明匹配数需要+1………………然后就没有然后了
等等!!有个重要问题!!怎么找增广路呢??
依次考虑每个左部节点,为其找到一个右部节点与之匹配。
一个右部节点能与之匹配,必满足一下两个条件之一:
1、这个节点尚未与任何左部节点匹配,此时直接把两个节点进行匹配
2、从该节点匹配的左部节点出发,可以找到一个未标记的右部节点与之匹配,此时给该节点打上标记,递归进入那个左部节点,为它寻找匹配节点

代码如下:

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>using namespace std;const int maxn=500+5,maxm=10000+5;int n,k,hd[maxn*2],to[maxm],nxt[maxm],ans,vis[maxn*2],cnt,pre[maxn*2];inline int read(void){    char ch=getchar();    int f=1,x=0;    while(!(ch>='0'&&ch<='9')){        if(ch=='-')            f=-1;        ch=getchar();    }    while(ch>='0'&&ch<='9')        x=x*10+ch-'0',ch=getchar();    return f*x;}inline void add(int x,int y){    to[cnt]=y;    nxt[cnt]=hd[x];    hd[x]=cnt++;}bool dfs(int u){    for(int i=hd[u];i!=-1;i=nxt[i]){        if(!vis[to[i]]){            vis[to[i]]=true;            if(pre[to[i]]==-1||dfs(pre[to[i]])){                pre[to[i]]=u;                return true;            }        }    }    return false;}signed main(void){    memset(hd,-1,sizeof(hd)),cnt=0;    memset(pre,-1,sizeof(pre));    n=read(),k=read();    while(k--)        add(read(),read()+n);    for(int i=1;i<=n;i++){        memset(vis,0,sizeof(vis));        if(dfs(i))            ans++;    }    cout<<ans<<endl;    return 0;}

接下来说无向边:
考虑每个未匹配的左部节点,为其寻找一个可以匹配的右部节点,右部节点的匹配条件相同
这里借用一下YOUSIKI童鞋的代码(自己懒得再写一遍了(⊙o⊙)…懒癌晚期……….),希望YOUSIKI童鞋不要收版权费>_<

#include<cstdio>#include<cstring>using namespace std;const int N=1005,M=20005;int n,m,hd[N],to[M],nt[M],tot,mch[N],chk[N];inline void addEdge(int x,int y){nt[tot]=hd[x],to[tot]=y,hd[x]=tot++;}inline bool dfs(int u){    for(int i=hd[u];~i;i=nt[i])if(!chk[to[i]]){        chk[to[i]]=1;if(mch[to[i]]==-1||dfs(mch[to[i]])){            mch[u]=to[i],mch[to[i]]=u;return true;}    }return false;}signed main(void){    scanf("%d%d",&n,&m);    memset(hd,-1,sizeof(hd)),tot=0;    for(int i=1,x,y;i<=m;i++)        scanf("%d%d",&x,&y),y+=n,        addEdge(x,y),addEdge(y,x);    int ans=0;memset(mch,-1,sizeof(mch));    for(int i=1;i<=n;i++)if(mch[i]==-1){        memset(chk,0,sizeof(chk));        if(dfs(i))ans++;    }printf("%d\n",ans);}//代码好短>_<.............

by >o< neighthorn

0 0