二分图的基础与基本应用:POJ 1469&&POJ 3041&&HDU 2255&&HDU1533
2013-08-18 00:20
459 查看
先把最基础的概念搞清楚:
二分图:有两组顶点,一组顶点记为 S1 ,另一组记为 S2 , S1 和 S2 没有公共的元素,并且所有的边都是连接 S1 和 S2 中的点的,对于 S1 和 S2 本身,它们内部的任何两个点都没有边相连,这样的无向图就叫二分图。
点覆盖集:即是一个点集,使得所有边至少有一个端点在集合里。
边覆盖集:即是一个边集,使得所有点都与集合里的边邻接。(其实我觉得这句话很难理解)
但是结合这句呢:在一个P*P的有向图中,最小路径覆盖=|P|-最大匹配数。(因为有匹配数的存在,本来要P个边的覆盖因此减小了)
二分图的最小顶点覆盖数等于最大匹配数。
匈牙利算法:主要二分图的最大匹配数。其实我觉得自学这东西不适合我,可能以前而到现在仍受体制化的影响,有老师真好(有依赖感了)。。并且我觉得有些自己理解花的时间比别人讲的时间要多很多很多!!!
等等。。。
除了点覆盖数还有边覆盖数。。。
下面M是G的一个匹配。
M-交错路:p是G的一条通路,如果p中的边为属于M中的边与不属于M但属于G中的边交替出现,则称p是一条M-交错路。
M-饱和点:对于v∈V(G),如果v与M中的某条边关联,则称v是M-饱和点,否则称v是非M-饱和点。
M-可增广路:p是一条M-交错路,如果p的起点和终点都是非M-饱和点,则称p为M-可增广路。(不要和流网络中的增广路径弄混了)
匈牙利算法:(用于求最大匹配数)
COURSES POJ 1469
一道匈牙利算法的模板题:
再就是求最小顶点覆盖数:(因为理论上就等于最大匹配数)
Asteroids POJ 3041
直接上匈牙利算法求最大匹配数就行了。。。
之后就是求最大权匹配:KM算法。。。
本人觉得有好多细节部分需要多次编写与记忆!
突出的一点要用顶标来计算,于是乎就多了两个数组,之后再交错树那里又多了一个数组!
然而判断方面因为有两顶标,就又多了一个数组。。。算算要开7个数组吧!!!(其中有两对)
奔小康赚大钱 HDU 2255
思路具体参见:/article/4990829.html
经过渊神的细心指导,总算弄懂清楚不少了!在此十分感谢!!!
根据KM算法,找不到匹配时,就要用交错树来凑成一个!(当然希望每进行一次,能凑成一个。。。于是就有slack[]数组了)
vix[]与viy[]是判断是否在交错树中的。。。
根据算法:在树中的lx[]减d,ly[]加d。。。(当然这个值d得在交错树外面找啊,因为进入交错树的都是有匹配的。。。)
最关键的:因为呢,slack[j] = lx[i] + ly[j] - w[i][j],而lx[]减小,其他的不变,(ly[]是交错树外面的),那么slack[j]也要减小d了。。。
时刻谨记着二分图是以左边为基点,向右边进行查找的(一旦左边的向下移动了,必定已经找到了。。。)
之后我发现求最大权匹配和最小权匹配是多么的不同啊!!
在建图的时候,(将每条边的权值变为负数。然后lx[i]初始化为-INT_MAX,结果输出-ans)!!,就可以得到最小权值。
这句话的重点在:要非常大啊!!
二分图:有两组顶点,一组顶点记为 S1 ,另一组记为 S2 , S1 和 S2 没有公共的元素,并且所有的边都是连接 S1 和 S2 中的点的,对于 S1 和 S2 本身,它们内部的任何两个点都没有边相连,这样的无向图就叫二分图。
点覆盖集:即是一个点集,使得所有边至少有一个端点在集合里。
边覆盖集:即是一个边集,使得所有点都与集合里的边邻接。(其实我觉得这句话很难理解)
但是结合这句呢:在一个P*P的有向图中,最小路径覆盖=|P|-最大匹配数。(因为有匹配数的存在,本来要P个边的覆盖因此减小了)
二分图的最小顶点覆盖数等于最大匹配数。
匈牙利算法:主要二分图的最大匹配数。其实我觉得自学这东西不适合我,可能以前而到现在仍受体制化的影响,有老师真好(有依赖感了)。。并且我觉得有些自己理解花的时间比别人讲的时间要多很多很多!!!
等等。。。
除了点覆盖数还有边覆盖数。。。
下面M是G的一个匹配。
M-交错路:p是G的一条通路,如果p中的边为属于M中的边与不属于M但属于G中的边交替出现,则称p是一条M-交错路。
M-饱和点:对于v∈V(G),如果v与M中的某条边关联,则称v是M-饱和点,否则称v是非M-饱和点。
M-可增广路:p是一条M-交错路,如果p的起点和终点都是非M-饱和点,则称p为M-可增广路。(不要和流网络中的增广路径弄混了)
匈牙利算法:(用于求最大匹配数)
COURSES POJ 1469
一道匈牙利算法的模板题:
#include<iostream> #include<algorithm> #include<stdio.h> #include<string.h> using namespace std; int a[110][310];//邻接矩阵。用来存储边的信息,连接或没连接 int vis[310],b1[310],m;//一个用来搜索的标记,一个记录点匹配点的编号 int dfs(int x) { int i; for(i=1;i<=m;i++) if(a[x][i]&&!vis[i])//连通了并且没被标记 { vis[i]=1;//进行标记 if(b1[i]==0||dfs(b1[i]))//没有被匹配或者前面的有增广路 { b1[i]=x;//进行记录 return 1;//返回能匹配 } } return 0;//返回没有被匹配 } int main() { int t,n,i,j,a2,a1,s; scanf("%d",&t); while(t--) { memset(a,0,sizeof(a)); scanf("%d%d",&n,&m); for(i=1;i<=n;i++) { scanf("%d",&a1); while(a1--) { scanf("%d",&a2); a[i][a2]=1; } } s=0; memset(b1,0,sizeof(b1)); for(i=1;i<=n;i++) { memset(vis,0,sizeof(vis));//每次都要更新 if(dfs(i)) s++; } if(s==n) printf("YES\n"); else printf("NO\n"); } return 0; }
再就是求最小顶点覆盖数:(因为理论上就等于最大匹配数)
Asteroids POJ 3041
直接上匈牙利算法求最大匹配数就行了。。。
#include<iostream> #include<algorithm> #include<stdio.h> #include<string.h> using namespace std; int a[505][505],vis[505],b1[505],n; int dfs(int x) { int i; for(i=1;i<=n;i++) { if(a[x][i]&&!vis[i]) { vis[i]=1; if(b1[i]==0||dfs(b1[i])) { b1[i]=x; return 1; } } } return 0; } int main() { int m,a1,a2,s,i; while(scanf("%d%d",&n,&m)!=EOF) { memset(a,0,sizeof(a)); while(m--) { scanf("%d%d",&a1,&a2); a[a1][a2]=1; } memset(b1,0,sizeof(b1)); s=0; for(i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); if(dfs(i)) s++; } printf("%d\n",s); } return 0; }
之后就是求最大权匹配:KM算法。。。
本人觉得有好多细节部分需要多次编写与记忆!
突出的一点要用顶标来计算,于是乎就多了两个数组,之后再交错树那里又多了一个数组!
然而判断方面因为有两顶标,就又多了一个数组。。。算算要开7个数组吧!!!(其中有两对)
奔小康赚大钱 HDU 2255
思路具体参见:/article/4990829.html
#include<iostream> #include<stdio.h> #include<algorithm> #include<string.h> using namespace std; int vix[310],viy[310],link[310],a[310][310],lx[310],ly[310],nx,ny,slack[310]; int inf=1000005; int dfs(int x)//跟匈牙利算法差不多 { int i,temp; vix[x]=1;//不同点 for(i=1;i<=ny;i++) { if(viy[i]) continue; temp=lx[x]+ly[i]-a[x][i];//找的是相等子图 if(temp==0) { viy[i]=1; if(link[i]==-1||dfs(link[i])) { link[i]=x; return 1; } } else if(slack[i]>temp) slack[i]=temp; } return 0; } int main() { int i,j,s,n; while(scanf("%d",&n)!=EOF) { nx=ny=n; for(i=1;i<=n;i++) for(j=1;j<=n;j++) scanf("%d",&a[i][j]); memset(lx,0,sizeof(lx)); memset(ly,0,sizeof(ly)); memset(link,-1,sizeof(link)); for(i=1;i<=nx;i++) for(j=1;j<=ny;j++) if(a[i][j]>lx[i]) lx[i]=a[i][j]; for(i=1;i<=nx;i++) { for(j=1;j<=nx;j++) slack[j]=inf; while(1) { memset(vix,0,sizeof(vix));//记录搜索的每次都更新 memset(viy,0,sizeof(viy));//如上 if(dfs(i))//有匹配 break; int d=inf;//下面是核心:凑成一个匹配出来 for(j=1;j<=ny;j++) if(!viy[j]&&d>slack[j]) d=slack[j]; for(j=1;j<=nx;j++) if(vix[j]) lx[j]-=d; for(j=1;j<=ny;j++) if(viy[j]) ly[j]+=d; else slack[j]-=d; } } s=0; for(i=1;i<=ny;i++) if(link[i]!=-1) s+=a[link[i]][i]; printf("%d\n",s); } return 0; }
经过渊神的细心指导,总算弄懂清楚不少了!在此十分感谢!!!
根据KM算法,找不到匹配时,就要用交错树来凑成一个!(当然希望每进行一次,能凑成一个。。。于是就有slack[]数组了)
vix[]与viy[]是判断是否在交错树中的。。。
根据算法:在树中的lx[]减d,ly[]加d。。。(当然这个值d得在交错树外面找啊,因为进入交错树的都是有匹配的。。。)
最关键的:因为呢,slack[j] = lx[i] + ly[j] - w[i][j],而lx[]减小,其他的不变,(ly[]是交错树外面的),那么slack[j]也要减小d了。。。
时刻谨记着二分图是以左边为基点,向右边进行查找的(一旦左边的向下移动了,必定已经找到了。。。)
之后我发现求最大权匹配和最小权匹配是多么的不同啊!!
在建图的时候,(将每条边的权值变为负数。然后lx[i]初始化为-INT_MAX,结果输出-ans)!!,就可以得到最小权值。
这句话的重点在:要非常大啊!!
Going Home HDU1533
废话也不说了,先化成二分图的形式。。。之后慢慢比对上面的代码!#include<iostream> #include<algorithm> #include<stdio.h> #include<math.h> #include<string.h> using namespace std; int vix[105],viy[105],link[105],b[105][105],lx[105],ly[105],d,slack[105]; int inf=100000005;//要非常大,少个零就超时!! struct line { int x; int y; }a1[105],b1[105]; int dfs(int x)//跟匈牙利算法差不多 { int i,temp; vix[x]=1;//不同点 for(i=1;i<=d;i++) { if(viy[i]) continue; temp=lx[x]+ly[i]-b[x][i];//找的是相等子图 if(temp==0) { viy[i]=1; if(link[i]==-1||dfs(link[i])) { link[i]=x; return 1; } } else if(slack[i]>temp) slack[i]=temp; } return 0; } int main() { char a[105][105]; int i,n,m,d1,j,s; while(scanf("%d%d",&n,&m)!=EOF) { if(m==0&&n==0) break; for(i=0;i<n;i++) scanf("%s",a[i]); d=0;d1=0; for(i=0;i<n;i++) for(j=0;j<m;j++) { if(a[i][j]=='m') { d++; a1[d].x=i; a1[d].y=j; } if(a[i][j]=='H') { d1++; b1[d1].x=i; b1[d1].y=j; } } for(i=1;i<=d;i++) for(j=1;j<=d;j++) b[i][j]=-abs(a1[i].x-b1[j].x)-abs(a1[i].y-b1[j].y);//全变成负值 memset(lx,-inf,sizeof(lx));//这里初始为负的,因为下面是负值,你要求较大值。。不然全为零了 memset(ly,0,sizeof(ly)); memset(link,-1,sizeof(link)); for(i=1;i<=d;i++) for(j=1;j<=d;j++) if(b[i][j]>lx[i])//赋负值的原因 lx[i]=b[i][j]; for(i=1;i<=d;i++) { for(j=1;j<=d;j++) slack[j]=inf; while(1) { memset(vix,0,sizeof(vix));//记录搜索的每次都更新 memset(viy,0,sizeof(viy));//如上 if(dfs(i))//有匹配 break; int h=inf;//下面是核心:凑成一个匹配出来 for(j=1;j<=d;j++) if(!viy[j]&&h>slack[j]) h=slack[j]; for(j=1;j<=d;j++) { if(vix[j]) lx[j]-=h; if(viy[j]) ly[j]+=h; else slack[j]-=h; } } } s=0; for(i=1;i<=d;i++) s+=b[link[i]][i]; printf("%d\n",-s); } return 0; }
相关文章推荐
- 二分图的拓展与应用:HDU 2819&&POJ 1486&&HDU 3488&&HDU1853
- 计算几何基础与应用:HDU 1348&&ZOJ 1648&&POJ 2398&&ZOJ 1010
- map的基本应用--hdu--1004&&poj--2643
- Hdu 1829 A Bug's Life && Poj 1182 食物链 (并查集偏移量的应用)
- HDU 1083&&POJ 1469解题报告
- HDU&POJ训练记录3 二分图KM算法
- hdu 1671&& poj 3630 (trie 树应用)
- 【二分图最大匹配】【匈牙利算法】poj1469 COURSES && poj2446 Chessboard
- POJ1469 二分图极度基础题
- poj2446 && poj1469 二分图最大匹配
- [POJ 1469]COURSES · 二分图
- poj 1469&&hdu 1803 Courses(最大匹配数)
- POJ 1904 King's Quest && HDU 4685 Prince and Princess (强联通解决二分图可行匹配问题)
- HDU 1671 & POJ 3630 Phone List(字典树基础题)
- POJ1469 COURSES 【二分图最大匹配·HK算法】
- HDU 1384 && POJ 1201--Intervals 【基础差分约束】
- 利用匈牙利算法&Hopcroft-Karp算法解决二分图中的最大二分匹配问题 例poj 1469 COURSES
- Poj 3692 & Hdu 2458 (08 合肥Online 二分图 最大团)
- HDU 2818&&POJ 1988 并查集简单应用
- HDU 1350 & HDU 1960 & POJ 2060 Taxi Cab Scheme【二分图之最小路径覆盖,经典】