http://acm.hdu.edu.cn/showproblem.php?pid=2063
设G=(V,E)是一个无向图。如顶点集V可分区为两个互不相交的子集V1,V2之并,并且图中每条边依附的两个顶点都分属于这两个不同的子集。则称图G为二分图。二分图也可记为G=(V1,V2,E)。
给定一个二分图G,在G的一个子图M中,M的边集{E}中的任意两条边都不依附于同一个顶点,则称M是一个匹配。选择这样的子集中边数最大的子集称为图的最大匹配问题(maximal matching problem)
图的点覆盖:寻找一个点集,使得图中每一条边至少有一点在该点集中
二分图的最小点覆盖 = 最大匹配
图的独立集:寻找一个点集,其中任意两点在图中无对应边
一般图的最大独立集是NP完全问题
二分图的最大独立集 = 图的点数-最大匹配
图的路径覆盖:用不相交路径覆盖有向无环图的所有顶点
二分图的最小路径覆盖 = 节点数-最大匹配
匈牙利算法:用增广路求最大匹配的方法
若P是图G中一条连通两个未匹配顶点的路径,并且属于M的边和不属于M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。(M为一个匹配)
由增广路的定义可以推出下述三个结论:
1-P的路径长度必定为奇数,第一条边和最后一条边都不属于M。
2-将M和P进行异或操作(去同存异)可以得到一个更大的匹配M’。
3-M为G的最大匹配当且仅当不存在M的增广路径。
算法描述:
(1)置M为空
(2)找出一条增广路径P,通过异或操作获得更大的匹配M’代替M
(3)重复(2)操作直到找不出增广路径为止
题目大意:有n个女生和m个男生,以及K种可能的组合方式,求最多能有几对组合;
[cpp] view plain copy print ?
- #include<stdio.h>
- #include<string.h>
- #define N 505
- int link[N],flag[N],map[1005][2],K;
- int Find(int x)
- {
- int i;
- for(i=0;i<K;i++){
- if(map[i][0]==x&&flag[map[i][1]]==0){
- flag[map[i][1]]=1;
- if(link[map[i][1]]==0 || Find(link[map[i][1]])==1){
- link[map[i][1]]=x;
- return 1;
- }
- }
- }
- return 0;
- }
- int main()
- {
- int n,m,i,cnt;
- while(scanf("%d",&K),K){
- scanf("%d%d",&n,&m);
- memset(link,0,sizeof(link));
- for(i=0;i<K;i++){
- scanf("%d%d",&map[i][0],&map[i][1]);
- }
- for(i=1,cnt=0;i<=n;i++){
- memset(flag,0,sizeof(flag));
- if(Find(i))
- cnt++;
- }
- printf("%d\n",cnt);
- }
- return 0;
- }
hdu 1498 二分图—最小点覆盖
分类: 图论
2013-03-27 11:16
299人阅读
收藏
举报
ACM 算法 二分图 最小点覆盖
http://acm.hdu.edu.cn/showproblem.php?pid=1498
/*
题目大意:有 n*n 的矩阵中放着 1-50 种气球,每次只能毁掉一行或者一列。求 k 此操作后那些气球不能被全部毁掉。
解题思路:对于每一种给定的color,当 map[i][j] = color 时可以认为存在一条 i—j 的路径,这样就可以构建一个二分图,二分图的两个点集分别是该颜色气球的横纵坐标;毁掉该 colo r的所有气球所需的次数就是该二分图的最小点覆盖。
什么是图的点覆盖
*/
[cpp] view plain copy print ?
- #include<stdio.h>
- #include<string.h>
- #define M 105
- #define N 55
- int map[M][M],link[M],flag[M],ans[N],n,k;
- int find(int i,int color)
- {
- int j;
- for(j=1;j<=n;j++){
- if(map[i][j]==color&&flag[j]==0){
- flag[j]=1;
- if(link[j]==0||find(link[j],color)==1){
- link[j]=i;
- return 1;
- }
- }
- }
- return 0;
- }
- int getnum(int color)
- {
- int i,sum;
- memset(link,0,sizeof(link));
- for(i=1,sum=0;i<=n;i++){
- memset(flag,0,sizeof(flag));
- if(find(i,color)==1)
- sum++;
- }
- return sum;
- }
- int main()
- {
- int i,j,t;
- while(scanf("%d%d",&n,&k),n||k){
- memset(map,0,sizeof(map));
- memset(ans,0,sizeof(ans));
- for(i=1;i<=n;i++){
- for(j=1;j<=n;j++){
- scanf("%d",&map[i][j]);
- }
- }
- for(i=1,t=0;i<=50;i++){
- if(getnum(i)>k)
- ans[t++]=i;
- }
- if(t==0)
- printf("-1\n");
- else{
- for(i=0;i<t;i++){
- printf(i==0?"%d":" %d",ans[i]);
- }
- printf("\n");
- }
- }
- return 0;
- }
hdu 2255 二分图—最优匹配
分类: 图论
2013-03-28 11:53
359人阅读
收藏
举报
ACM KM算法 二分图 最优匹配 图论
http://acm.hdu.edu.cn/showproblem.php?pid=2255
/*
最优匹配:使二分图的边权和最大的完备匹配。(如果二分图的两个点集不相等可以通过加 ’点‘ 和 ’权值为0的边‘ 实现转化)
相关概念
可行顶标:若L(x)是顶点x对应的顶标值。可行顶标对于图中的每条边(x,y)都有L(x)+L(y)>=w(x,y)
相等子图:只包含L(x)+L(y)=w(x,y)的边的子图
设顶点Xi的顶标为a[i],顶点Yi的顶标为b[i]
ⅰ.初始时,a[i]为与Xi相关联的边的最大权值,b[j]=0,保证a[i]+b[j]>=w(i,j)成立
ⅱ.当相等子图中不包含完备匹配时,就适当修改顶标以扩大相等子图,直到找到完备匹配为止
修改顶标的方法:当从 Xi 开始寻找交错路失败后,得到一棵交错树,对交错树中 X 顶点的顶标减少 d 值, Y 顶点的顶标增加 d 值。
对于图中所有的边 (i,j) 有:
i和j都不在交错树中,边(i,j)仍然不属于相等子图
i不在交错树中,j在交错树中,a[i]+b[j]扩大,边(i,j)不属于相等子图
i在交错树,j不在交错树中,边(i,j)有可能加入到相等子图中
为了使a[i]+b[j]>=w(i,j)始终成立,且至少有一条边加入到相等子图中,d=min{a[i]+b[j]-w(i,j)},其中 i 在交错树中, j 不在交错树中。
由于在算法过程一直保持顶标的可行性,所以任意一个匹配的权值和肯定小于等于所有结点的顶标之和,又因为在扩大相等子图过程中优先加入了权值大大边,所以在相等子图中找到的第一个完备匹配就是最优匹配。
*/
题目大意:有n个人和n件物品,w[i][j]表示第 i 个人对第 j 件物品的出价,求这些物品能得到的最大价值;
[cpp] view plain copy print ?
- #include<stdio.h>
- #include<string.h>
- #define N 305
- #define Max 1000000
- int w[N][N],linky[N];
- int lx[N],ly[N];
- int visx[N],visy[N];
- int n,low;
- int Find(int x)
- {
- int y,t;
- visx[x]=1;
- for(y=0;y<n;y++){
- if(visy[y])continue;
- t=lx[x]+ly[y]-w[x][y];
- if(t==0){
- visy[y]=1;
- if(linky[y]==-1||Find(linky[y])){
- linky[y]=x;
- return 1;
- }
- }
- else{
- if(low>t)low=t;
- }
- }
- return 0;
- }
- void KM()
- {
- int i,x;
- for(x=0;x<n;x++){
- while(1){
- memset(visx,0,sizeof(visx));
- memset(visy,0,sizeof(visy));
- low=Max;
- if(Find(x))break;
- for(i=0;i<n;i++){
- if(visx[i])
- lx[i]-=low;
- if(visy[i])
- ly[i]+=low;
- }
- }
- }
- }
- int main()
- {
- int i,j,sum;
- while(scanf("%d",&n)!=EOF){
- memset(lx,0,sizeof(lx));
- memset(ly,0,sizeof(ly));
- memset(linky,-1,sizeof(linky));
- for(i=0;i<n;i++){
- for(j=0;j<n;j++){
- scanf("%d",&w[i][j]);
- if(lx[i]<w[i][j])
- lx[i]=w[i][j];
- }
- }
- KM();
- for(i=0,sum=0;i<n;i++){
- sum+=w[linky[i]][i];
- }
- printf("%d\n",sum);
- }
- return 0;
- }
#include<stdio.h>
#include<string.h>
#define N 305
#define Max 1000000
int w[N][N],linky[N]; //数组 w 记录边权,数组 linky 记录顶点 y 的匹配;
int lx[N],ly[N]; //数组 lx 和 ly 分别记录顶点 x 和 y 的顶标值;
int visx[N],visy[N]; //数组 visx 和 visy 分别记录顶点 x 和 y 是否被访问;
int n,low; //low 记录每次顶标需要变化的值;
int Find(int x)
{
int y,t;
visx[x]=1;
for(y=0;y<n;y++){
if(visy[y])continue;
t=lx[x]+ly[y]-w[x][y];
if(t==0){
visy[y]=1;
if(linky[y]==-1||Find(linky[y])){
linky[y]=x;
return 1;
}
}
else{
if(low>t)low=t;
}
}
return 0;
}
void KM()
{
int i,x;
for(x=0;x<n;x++){
while(1){
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
low=Max;
if(Find(x))break;
for(i=0;i<n;i++){
if(visx[i])
lx[i]-=low;
if(visy[i])
ly[i]+=low;
}
}
}
}
int main()
{
int i,j,sum;
while(scanf("%d",&n)!=EOF){
memset(lx,0,sizeof(lx));
memset(ly,0,sizeof(ly));
memset(linky,-1,sizeof(linky));
for(i=0;i<n;i++){
for(j=0;j<n;j++){
scanf("%d",&w[i][j]);
if(lx[i]<w[i][j])
lx[i]=w[i][j];
}
}
KM();
for(i=0,sum=0;i<n;i++){
sum+=w[linky[i]][i];
}
printf("%d\n",sum);
}
return 0;
}
分享到:
hdu 2444 二分图判定+最大匹配
分类: 图论
2013-05-03 08:11
238人阅读
收藏
举报
ACM C 图论 二分图判定 最大匹配
http://acm.hdu.edu.cn/showproblem.php?pid=2444
题目大意:有n个人之间有m对互相认识,问能否将所有人分成两组,同一组的任意两人互不认识。若不能分组输出“No”,若能分组计算并输出有最多有多少对人互相认识;
思路:先用DFS进行染色,判断能否分成两组组(即能否构成二分图),如果可以求二分图的最大匹配;
[cpp] view plain copy print ?
- #include<iostream>
- #include<stdio.h>
- #include<string.h>
- #include<stdlib.h>
- #define N 202
- using namespace std;
- int map[N][N],link[N],visit[N],color[N];
- bool flag;
- bool DFS(int i,int n,int c)
- {
- int j;
- for(j=1;j<=n;j++){
- if(map[i][j]==1){
- if(color[j]==c)
- continue;
- if(color[j]==0){
- color[j]=c;
- flag=DFS(j,n,-c);
- }
- else{
- return false;
- }
- if(!flag)
- return false;
- }
- }
- return true;
- }
- bool find(int x,int n)
- {
- int y;
- for(y=1;y<=n;y++){
- if(map[x][y]==1 && visit[y]==0){
- visit[y]=1;
- if(link[y]==-1 || find(link[y],n)){
- link[y]=x;
- return true;
- }
- }
- }
- return false;
- }
- int main()
- {
- int n,m,a,b,i,cnt;
- while(scanf("%d%d",&n,&m)!=EOF){
- flag=true;
- memset(map,0,sizeof(map));
- memset(link,-1,sizeof(link));
- memset(color,0,sizeof(color));
- while(m--){
- scanf("%d%d",&a,&b);
- map[a][b]=map[b][a]=1;
- }
- for(i=1;i<=n;i++){
- if(color[i]==0)
- color[i]=1;
- flag=DFS(i,n,-color[i]);
- if(!flag)break;
- }
- if(!flag){
- printf("No\n");
- continue;
- }
- for(i=1,cnt=0;i<=n;i++){
- memset(visit,0,sizeof(visit));
- if(find(i,n))
- cnt++;
- }
- printf("%d\n",cnt/2);
- }
- return 0;
- }