您的位置:首页 > 其它

二分图 匈牙利算法应用

2015-11-26 13:02 519 查看
匈牙利算法是kuangbin的

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;
}
但是其实精髓还是不由完全掌握,以后遇见还要注意......
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: