二分图 匈牙利算法应用
2015-11-26 13:02
519 查看
匈牙利算法是kuangbin的
poj 1274 poj 2239 //水题
poj 2584
这个还是有点想法的,把每种衣服拆成n件衣服(每件衣服都是一个单独的节点),然后依次连接
下面是代码,思路还是比较清晰的
poj 2536
这题是水题,可是老是A不了,我也不知道为什么,我的思路和代码风格完全和题解并没有太大区别
poj 2446
这题我是这样写的
然后老是WA怎么也改不过来......
然后我就看了看kuangbin的思路,没办法,改的和他一样过了......
其实我也不知道我之前的思路到低哪里不对了,现在也不知道
以下是正确代码
poj 3041
这个题我要好好写写题解(其实是看别人的)我自己想了想,感觉和二分图实在是扯不上关系......
这是别人的说法
这个题目属于标准的图论题目,需要适当的转换,建立适当的图来进行结题。建立二分图,设置x坐标作为A点集,y坐标作为B点集。那么每个点转化成连接A、B点集的边。问题就转化成最小点覆盖问题,即若选定一个点,那么于此点相连的所有边都被选定,求满足选定所有边的最少的点集。最小点覆盖问题可以用二分图的最大匹配来解。最小点数=最大匹配数(此处不在证明,详情请看上篇文章)。这样通过构建二分图求解最大匹配数,来求解该问题的最小点覆盖。
然后这题就是大水题了......
这个题之前见过,总想着是不是个贪心,前人经验证明贪心不可做
以下是代码
poj 1325
这题不难,不过有个地方需要注意
就是初始状态两个机器都是状态0,所以有个事要注意,就是所有和0相连的的边都要删掉......(就因为这个WA了几次)
poj 2226
这个题挺难想的
我是看别人的题解看出来的
以下是别人的分析
题意:R*C的矩阵表示一块田地,'*'表示湿地,'.'表示草地。现在FJ要在田地上铺木板盖掉所有的湿地,露出所有的草地。每块木板的宽度为1,长度为任意长。问FJ最少用几块木板就可以完成任务?
思路:看了半天,实在是没思路,看了看别人的报告才明白。把它转化为一个二分图,求最小点覆盖。
但如何构成二分图呢??
就是把每一行中不相连的一块区域看成一个点,同理每一列也一样。然后就有点像3041那题一样。求它的最小点覆盖
*.*. 按行1 0 2 0 按列 1 0 4 0
.*** 0 3 3 3 0 3 4 5
***. 4 4 4 0 2 3 4 0
..*. 0 0 5 0 0 0 4 0
下面是代码
poj 1274 poj 2239 //水题
poj 2584
这个还是有点想法的,把每种衣服拆成n件衣服(每件衣服都是一个单独的节点),然后依次连接
下面是代码,思路还是比较清晰的
#include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; /* ************************************************************************** //二分图匹配(匈牙利算法的DFS实现) //初始化:g[][]两边顶点的划分情况 //建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配 //g没有边相连则初始化为0 //uN是匹配左边的顶点数,vN是匹配右边的顶点数 //调用:res=hungary();输出最大匹配数 //优点:适用于稠密图,DFS找增广路,实现简洁易于理解 //时间复杂度:O(VE) //***************************************************************************/ //顶点编号从0开始的 const int MAXN=400; int uN,vN;//u,v数目 int g[MAXN][MAXN]; int linker[MAXN]; bool used[MAXN]; bool dfs(int u)//从左边开始找增广路径 { int v; for(v=0;v<vN;v++)//这个顶点编号从0开始,若要从1开始需要修改 if(g[u][v]&&!used[v]) { used[v]=true; if(linker[v]==-1||dfs(linker[v])) {//找增广路,反向 linker[v]=u; return true; } } return false;//这个不要忘了,经常忘记这句 } int hungary()//xiongyali { int res=0; int u; memset(linker,-1,sizeof(linker)); for(u=0;u<uN;u++) { memset(used,0,sizeof(used)); if(dfs(u)) res++; } return res; } //******************************************************************************/ int F[100],A[100],tmp[50]; int judge(char t){ if(t=='S')return 1; else if(t=='M')return 2; else if(t=='L')return 3; else if(t=='X')return 4; else return 5; } int main() { int v; int k,k1,k2; char str[30]; while(~scanf("%s",&str)&&strcmp(str,"ENDOFINPUT")!=0) { scanf("%d",&uN); memset(g,0,sizeof(g)); getchar(); for(int i=0;i<uN;i++) { char t1,t2; scanf("%c%c",&t1,&t2); getchar(); F[i]=judge(t1); A[i]=judge(t2); } memset(tmp,0,sizeof(tmp)); for(int i=1;i<=5;i++){ scanf("%d",&tmp[i]); tmp[i]+=tmp[i-1]; } for(int i=0;i<uN;i++){ for(int j=F[i];j<=A[i];j++){ for(int k=tmp[j-1]+1;k<=tmp[j];k++)g[i][k]=1; } } vN=tmp[5]+20;//刚开始这里写的是6*12+11,WA,后来又+5,莫名其妙AC了,我也不知道为什么...... int re=hungary(); getchar(); scanf("%s",&str); getchar(); if(re==uN)printf("T-shirts rock!\n"); else printf("I'd rather not wear a shirt anyway...\n"); } return 0; }
poj 2536
这题是水题,可是老是A不了,我也不知道为什么,我的思路和代码风格完全和题解并没有太大区别
poj 2446
这题我是这样写的
然后老是WA怎么也改不过来......
#include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; /* ************************************************************************** //二分图匹配(匈牙利算法的DFS实现) //初始化:g[][]两边顶点的划分情况 //建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配 //g没有边相连则初始化为0 //uN是匹配左边的顶点数,vN是匹配右边的顶点数 //调用:res=hungary();输出最大匹配数 //优点:适用于稠密图,DFS找增广路,实现简洁易于理解 //时间复杂度:O(VE) //***************************************************************************/ //顶点编号从0开始的 const int MAXN=1500+50; int uN,vN;//u,v数目 int g[MAXN][MAXN]; int linker[MAXN]; bool used[MAXN]; int maze[500][500]; bool dfs(int u)//从左边开始找增广路径 { int v; for(v=0;v<vN;v++)//这个顶点编号从0开始,若要从1开始需要修改 if(g[u][v]&&!used[v]) { used[v]=true; if(linker[v]==-1||dfs(linker[v])) {//找增广路,反向 linker[v]=u; return true; } } return false;//这个不要忘了,经常忘记这句 } int hungary()//xiongyali { int res=0; int u; memset(linker,-1,sizeof(linker)); for(u=0;u<uN;u++) { memset(used,0,sizeof(used)); if(dfs(u))res++; } return res; } //******************************************************************************/ int n,m; int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; bool ok(int x,int y){ if(x<=0||x>n||y<=0||y>m||maze[x][y]==-1)return false; return true; } void deal(){ uN=n*m+5; vN=n*m+5; memset(g,0,sizeof(g)); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ for(int k=0;k<4;k++){ int x=i+dir[k][0],y=j+dir[k][1]; if(ok(x,y))g[(i-1)*m+j-1][(x-1)*m+y-1]=1; } } } } int main() { int k,x,y; while(~scanf("%d%d%d",&n,&m,&k)){ memset(maze,0,sizeof(maze)); for(int i=1;i<=k;i++){ scanf("%d%d",&y,&x); maze[x][y]=-1; } deal(); int re=hungary(); if((n*m-k)%2)printf("NO\n"); else if(re+k==n*m)printf("YES\n"); else printf("NO\n"); } }
然后我就看了看kuangbin的思路,没办法,改的和他一样过了......
其实我也不知道我之前的思路到低哪里不对了,现在也不知道
以下是正确代码
#include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; const int MAXN=2500+50; int uN,vN;//u,v数目 int g[MAXN][MAXN]; int linker[MAXN]; bool used[MAXN]; int maze[500][500],num[50][50]; bool dfs(int u)//从左边开始找增广路径 { int v; for(v=0;v<vN;v++)//这个顶点编号从0开始,若要从1开始需要修改 if(g[u][v]&&!used[v]) { used[v]=true; if(linker[v]==-1||dfs(linker[v])) {//找增广路,反向 linker[v]=u; return true; } } return false;//这个不要忘了,经常忘记这句 } int hungary()//xiongyali { int res=0; int u; memset(linker,-1,sizeof(linker)); for(u=0;u<uN;u++) { memset(used,0,sizeof(used)); if(dfs(u))res++; } return res; } //******************************************************************************/ int n,m; int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; bool ok(int x,int y){ if(x<=0||x>n||y<=0||y>m||maze[x][y]==-1)return false; return true; } int main() { int k,x,y; while(~scanf("%d%d%d",&n,&m,&k)){ memset(maze,0,sizeof(maze)); memset(num,0,sizeof(num)); memset(g,0,sizeof(g)); for(int i=1;i<=k;i++){ scanf("%d%d",&y,&x); maze[x][y]=-1; } int tol=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(maze[i][j]!=-1)num[i][j]=tol++; } } uN=vN=tol; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(maze[i][j]!=-1) { int u=num[i][j]; if(i-1>0&&maze[i-1][j]!=-1)g[u][num[i-1][j]]=1; if(j-1>0&&maze[i][j-1]!=-1)g[u][num[i][j-1]]=1; if(i+1<=n&&maze[i+1][j]!=-1)g[u][num[i+1][j]]=1; if(j+1<=m&&maze[i][j+1]!=-1)g[u][num[i][j+1]]=1; } int re=hungary(); if((n*m-k)%2)printf("NO\n"); else if(re==tol)printf("YES\n"); else printf("NO\n"); } }
poj 3041
这个题我要好好写写题解(其实是看别人的)我自己想了想,感觉和二分图实在是扯不上关系......
这是别人的说法
这个题目属于标准的图论题目,需要适当的转换,建立适当的图来进行结题。建立二分图,设置x坐标作为A点集,y坐标作为B点集。那么每个点转化成连接A、B点集的边。问题就转化成最小点覆盖问题,即若选定一个点,那么于此点相连的所有边都被选定,求满足选定所有边的最少的点集。最小点覆盖问题可以用二分图的最大匹配来解。最小点数=最大匹配数(此处不在证明,详情请看上篇文章)。这样通过构建二分图求解最大匹配数,来求解该问题的最小点覆盖。
然后这题就是大水题了......
这个题之前见过,总想着是不是个贪心,前人经验证明贪心不可做
以下是代码
#include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; const int MAXN=2500+50; int uN,vN;//u,v数目 int g[MAXN][MAXN]; int linker[MAXN]; bool used[MAXN]; int maze[500][500],num[50][50]; bool dfs(int u)//从左边开始找增广路径 { int v; for(v=0;v<vN;v++)//这个顶点编号从0开始,若要从1开始需要修改 if(g[u][v]&&!used[v]) { used[v]=true; if(linker[v]==-1||dfs(linker[v])) {//找增广路,反向 linker[v]=u; return true; } } return false;//这个不要忘了,经常忘记这句 } int hungary()//xiongyali { int res=0; int u; memset(linker,-1,sizeof(linker)); for(u=0;u<uN;u++) { memset(used,0,sizeof(used)); if(dfs(u))res++; } return res; } //******************************************************************************/ int n,m; int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}}; bool ok(int x,int y){ if(x<=0||x>n||y<=0||y>m||maze[x][y]==-1)return false; return true; } int main() { int n,k,x,y; while(~scanf("%d%d",&n,&k)){ memset(g,0,sizeof(g)); uN=n+1,vN=n+1; while(k--){ scanf("%d%d",&x,&y); g[x--][y--]=1; } printf("%d\n",hungary()); } }
poj 1325
这题不难,不过有个地方需要注意
就是初始状态两个机器都是状态0,所以有个事要注意,就是所有和0相连的的边都要删掉......(就因为这个WA了几次)
poj 2226
这个题挺难想的
我是看别人的题解看出来的
以下是别人的分析
题意:R*C的矩阵表示一块田地,'*'表示湿地,'.'表示草地。现在FJ要在田地上铺木板盖掉所有的湿地,露出所有的草地。每块木板的宽度为1,长度为任意长。问FJ最少用几块木板就可以完成任务?
思路:看了半天,实在是没思路,看了看别人的报告才明白。把它转化为一个二分图,求最小点覆盖。
但如何构成二分图呢??
就是把每一行中不相连的一块区域看成一个点,同理每一列也一样。然后就有点像3041那题一样。求它的最小点覆盖
*.*. 按行1 0 2 0 按列 1 0 4 0
.*** 0 3 3 3 0 3 4 5
***. 4 4 4 0 2 3 4 0
..*. 0 0 5 0 0 0 4 0
下面是代码
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int n,m,cnt1,cnt2,map[2520][2520],linker[2520],vis[2520]; int g1[60][60],g2[60][60],tg[60][60]; char str[60][60]; int DFS(int u){ int v; for(v=1;v<=cnt2;v++) if(map[u][v] && !vis[v]){ vis[v]=1; if(linker[v]==-1 || DFS(linker[v])){ linker[v]=u; return 1; } } return 0; } int Hungary(){ int u,ans=0; memset(linker,-1,sizeof(linker)); for(u=1;u<=cnt1;u++){ memset(vis,0,sizeof(vis)); if(DFS(u)) ans++; } return ans; } int main(){ //freopen("input.txt","r",stdin); while(~scanf("%d%d",&n,&m)){ for(int i=0;i<n;i++) scanf("%s",str[i]); memset(g1,0,sizeof(g1)); memset(g2,0,sizeof(g2)); cnt1=0; for(int i=0;i<n;i++) for(int j=0;j<m;j++){ if(str[i][j]=='*'){ if(j==0) g1[i][j]=++cnt1; else if(str[i][j-1]=='*') g1[i][j]=g1[i][j-1]; else g1[i][j]=++cnt1; } } cnt2=0; for(int j=0;j<m;j++) //注意这里 for(int i=0;i<n;i++){ if(str[i][j]=='*'){ if(i==0) g2[i][j]=++cnt2; else if(str[i-1][j]=='*') g2[i][j]=g2[i-1][j]; else g2[i][j]=++cnt2; } } memset(map,0,sizeof(map)); for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(str[i][j]=='*') map[g1[i][j]][g2[i][j]]=1; int ans=Hungary(); printf("%d\n",ans); } return 0; }但是其实精髓还是不由完全掌握,以后遇见还要注意......
相关文章推荐
- 递归与分治之棋盘覆盖问题
- centos中的qt设计师所在的包
- 搭建用友开发环境(基于碧桂园的nchome)
- php添加mysqli扩展
- c语言:使用for循环编写一个判断闰年的程序
- AsyncTask的onPostExecute方法没有被系统回调的解决方案
- 递归与分治之棋盘覆盖问题
- 许家印:思想工作与奖罚制度同等重要
- 数据备份学习
- 类文件结构
- hibernate save和update以及saveOrUpdate区别
- C++ const变量使用技巧总结
- 远程数据库操作整理
- GUI矩形、椭圆、线、框架
- 架构设计思想
- ubuntu添加中文输入法
- HTML5实战应用:如何让表单验证更友好
- Centos eclipse C++ 环境配置
- 数据库分页语句(mysql,oracle,sqlserver,DB2)
- 解决SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT'OpenRowset/OpenDatasource' 的访问的方法