二分图最大匹配

a 夏天 提交于 2019-12-24 01:13:51

HDU 1150:

http://acm.hdu.edu.cn/showproblem.php?pid=1150

  最小覆盖点 == 最大匹配数(选取最少的点数,使这些点和所有的边都有关联——把所有的边的覆盖)

      两台机器,有n和m个工作模式,起始工作模式都为0,现在有k件工作,第i件工作可分别在两个机器上用各自的模式工作,但换模式要重启,问重启的最小次数。

      写的时候因为是找二分最大匹配的题目时找到写的,想到了二分上去,也知道是求最小覆盖点 == 最大匹配数,但不是很能理解,先把代码写了再说。

      写的时候注意起始模式是0,所以换模式时把0的排除再外。(因为这个原因错了很多次)

一:邻接阵做法

代码
#include<iostream>using namespace std;int n,m,k;int map[105][105];  //记录X,Y对应点可否连接int vis[105];    //每次找增广路时对Y中点是否访问int dir[105];   //Y中点匹配的X中点的位置int find(int a){    int i;    for(i=0;i<m;i++)    {        if(map[a][i]==1 && vis[i] == 0)        {            vis[i] = 1;            if(dir[i] == -1 || find(dir[i]))            {                dir[i] = a;                return 1;            }        }    }    return 0;}int main(){    int i,x,y;    while(scanf("%d",&n)!=EOF && n!=0)    {        memset(map,0,sizeof(map));        memset(dir,-1,sizeof(dir));        scanf("%d%d",&m,&k);        while(k--)        {            scanf("%d%d%d",&i,&x,&y);            if(x && y)                map[x][y] = 1; //应该不能再用map[y][x] = 1        }        int sum = 0;        for(i=0;i<n;i++)        {            memset(vis,0,sizeof(vis));            if(find(i))                sum++;        }            printf("%d\n",sum);    }    return 0;}

二:动态邻接表做法.

代码
#include<iostream>using namespace std;int n,m,k;int vis[105];    //每次找增广路时对Y中点是否访问int dir[105];   //Y中点匹配的X中点的位置struct edge{    int from;    int to;    edge* next;    edge()    {        from = to = 0;        next = NULL;    }};edge* List[105];void add_edge(int f,int t){    edge* node = new edge();    node->from = f;    node->to = t;    node->next = List[f];    List[f] = node;}int find(edge* node){    while(1)    {        if(node == NULL)        {            break;        }        int i = node->to;        if(vis[i] == 0)        {            vis[i] = 1;            if(dir[i] == -1 || find(List[dir[i]]))            {                dir[i] = node->from;                return 1;            }        }        node = node->next;    }    return 0;}int main(){    int i,x,y;    while(scanf("%d",&n)!=EOF && n!=0)    {        for(i=0;i<=n;i++)//链表清空,一开始没清空,错了很多次        {            List[i] = NULL;        }        memset(dir,-1,sizeof(dir));        scanf("%d%d",&m,&k);        while(k--)        {            scanf("%d%d%d",&i,&x,&y);            if(x && y)                add_edge(x,y);        }        int sum = 0;        for(i=1;i<=n;i++)        {            memset(vis,0,sizeof(vis));            if(find(List[i]))                sum++;        }        printf("%d\n",sum);    }    return 0;}

HDU 1151:

http://acm.hdu.edu.cn/showproblem.php?pid=1151

     最小路径覆盖(选取最少的边覆盖所有的点) == 节点数 - 最大匹配数


代码

#include <iostream>using namespace std;#define MAXN 125int vis[MAXN];int link[MAXN];int n,m;struct edge{    int from;    int to;    edge* next;    edge()    {        from = to = 0;        next = NULL;    }};edge* List[MAXN];void add_edge(int f,int t){    edge* node = new edge();    node->from = f;    node->to = t;    node->next = List[f];    List[f] = node;}bool find(edge* node){    while(1)    {        if(node == NULL)            break;        int i = node->to;        if(vis[i] == 0)        {            vis[i] = 1;            if(link[i] == 0 || find(List[link[i]]))            {                link[i] = node->from;                return 1;            }        }        node = node->next;    }    return 0;}int main(){    int i;    int t,x,y;    scanf("%d",&t);    while(t--)    {        memset(link,0,sizeof(link));        scanf("%d%d",&n,&m);        for(i=0;i<=n;i++)        {            List[i] = NULL;        }        for(i=0;i<m;i++)        {            scanf("%d%d",&x,&y);            add_edge(x,y);        }        int sum = 0;        for(i=1;i<=n;i++)        {            memset(vis,0,sizeof(vis));            if(find(List[i]))                sum++;        }        printf("%d\n",n-sum);    }    return 0;}

 

HDU 1068:

http://acm.hdu.edu.cn/showproblem.php?pid=1068

  二分图最大独立集=顶点数-二分图最大匹配

代码

#include <iostream>using namespace std;const long max_edge = 100005;const long max_point = 1005;int n;int Index;struct node{    int y;    int next;}stu[max_edge];int pre[max_point];//以该点为起点的第一条在stu中的存储位置int vis[max_point];//Y中结点的访问情况int link[max_point];//Y中与X中配对的情况void add_edge(int a,int b){    stu[Index].y = b;    stu[Index].next = pre[a];    pre[a] = Index++;}void Init(){    int i,j,s,t,m;    char c;    Index = 1;    memset(pre,-1,sizeof(pre));    memset(link,-1,sizeof(link));    for(i=0;i<n;i++)    {        cin>>s>>c>>c>>m>>c;        for(j=0;j<m;j++)        {            scanf("%d",&t);            add_edge(s,t);        }    }}bool find(int dir){    int i;    for(i=pre[dir]; i!=-1; i=stu[i].next)    {        int ii = stu[i].y;        if(vis[ii] == 0)        {            vis[ii] = 1;            if(link[ii] == -1 || find(link[ii]))            {                link[ii] = dir;                return true;            }        }    }    return false;}void Play(){    int i;    int ans = 0;    for(i=0;i<n;i++)    {        memset(vis,0,sizeof(vis));        if(find(i))            ans++;    }    printf("%d\n",n-ans/2);}int main(){    while(scanf("%d",&n)!=EOF)    {        Init();        Play();    }    return 0;}

HDU 1281:

http://acm.hdu.edu.cn/showproblem.php?pid=1281

写的时候写搓了,一个小错误,find1递归的时候用了find函数,很是郁闷!

当数据量小的时候可以不用静态连接表存储,毕竟连接阵用起来比连接表方便!

代码
#include <iostream>using namespace std;const long max_point = 1005;const long max_edge = 100005;int n,m,k;int cas = 0;int mat[max_point][max_point];int vis[max_point];int linky[max_point];int linkx[max_point];void Init(){    int i;    int a,b;    memset(mat,0,sizeof(mat));    for(i=0;i<k;i++)    {        scanf("%d%d",&a,&b);        mat[a][b] = 1;    }}bool find(int dir,bool flag){    int i;    for(i=1;i<=m;i++)    {        if(mat[dir][i] && !vis[i])        {            vis[i] = 1;            if(linky[i]==-1 || find(linky[i],flag))            {                if(flag)                {                    linkx[dir] = i;                    linky[i] = dir;                }                return true;            }        }    }    return false;}bool can(){    int i;    for(i=1;i<=n;i++)    {        if(linkx[i] == -1)        {            memset(vis,0,sizeof(vis));            if(find(i,0))            {                return true;            }        }    }    return false;}void Play(){    int i,j;    int ans = 0;        memset(linkx,-1,sizeof(linkx));    memset(linky,-1,sizeof(linky));    for(i=1;i<=n;i++)    {        memset(vis,0,sizeof(vis));        if(find(i,1))            ans++;    }    int ans1 = 0;    for(i=1;i<=n;i++)    {        if(linkx[i] != -1)        {            int tmp = linkx[i];            linkx[i] = -1;            linky[tmp] = -1;            mat[i][tmp] = 0;            if(!can())                ans1++;            mat[i][tmp] = 1;            linky[tmp] = i;            linkx[i] = tmp;        }    }    printf("Board %d have %d important blanks for %d chessmen.\n",++cas,ans1,ans);}int main(){    while(scanf("%d%d%d",&n,&m,&k)!=EOF)    {        Init();        Play();    }    return 0;}

HDU 1498:

http://acm.hdu.edu.cn/showproblem.php?pid=1498

感觉来了,这题没花什么精力,一杯咖啡的时候想了出来。对每个颜色,求最小点覆盖,如果大于k,则不能在k时间内完成,输出;否则可以完成。

代码
#include <iostream>using namespace std;int n,k;const long max_color = 55;const long maxn = 105;int Hash[max_color];int map[max_color][maxn][maxn];int vis[maxn];int linkx[maxn],linky[maxn];void Init(){    int i,j;    memset(map,0,sizeof(map));    memset(Hash,0,sizeof(Hash));    for(i=0;i<n;i++)    {        for(j=0;j<n;j++)        {            int color;            scanf("%d",&color);            map[color][i][j] = 1;            Hash[color] = 1;        }    }}bool find(int dir,int u){    int i,j;    for(i=0;i<n;i++)    {            if(1 == map[dir][u][i])        {            if(0 == vis[i])            {                vis[i] = 1;                if(linky[i] == -1 || find(dir,linky[i]))                {                    linky[i] = u;                    linkx[u] = i;                    return true;                }            }        }        }    return false;}int MaxMatch(int dir){    int i;    int sum = 0;    memset(linkx,-1,sizeof(linkx));    memset(linky,-1,sizeof(linky));    for(i=0;i<n;i++)    {        if(-1 == linkx[i])        {            memset(vis,0,sizeof(vis));            if(find(dir,i))            {                sum++;            }        }    }    return sum;}void Play(){    int i;    int flag = 0;    for(i=1;i<=50;i++)    {//对应每种颜色        if(1 == Hash[i])        {            int num = MaxMatch(i);            /*if(flag != 0)            {//这个放在外面,PE了一次                printf(" ");            }*/            if(num > k)            {                if(flag != 0)                {                    printf(" ");                }                printf("%d",i);                flag = 1;            }        }    }    if(0 == flag)    {        printf("-1");    }    printf("\n");}int main(){    while(scanf("%d%d",&n,&k)!=EOF && (n!=0 || k!=0))    {        Init();        Play();    }    return 0;}

HDU 1528:

http://acm.hdu.edu.cn/showproblem.php?pid=1528

简单题,卡片为点,大小关系为边,求最大匹配。 

代码
#include <iostream>#include <map>using namespace std;const long maxn = 30;int k;map<char,int> M;int num_Adam[maxn],num_Eve[maxn];int linkx[maxn],linky[maxn];int mat[maxn][maxn],vis[maxn],pre[maxn];void Init(){    int i,j;    char c1,c2;    memset(mat,0,sizeof(mat));    M['2'] = 0;M['3'] = 1;M['4'] = 2;M['5'] = 3;M['6'] = 4;    M['7'] = 5;M['8'] = 6;M['9'] = 7;M['T'] = 8;M['J'] = 9;    M['Q'] = 10;M['K'] = 11;M['A'] = 12;    M['H'] = 3;M['S'] = 2;M['D'] = 1;M['C'] = 0;        scanf("%d",&k);    for(i=0;i<k;i++)    {        cin>>c1>>c2;        num_Adam[i] = M[c1] * 4 + M[c2];    }    for(i=0;i<k;i++)    {        cin>>c1>>c2;        num_Eve[i] = M[c1] * 4 + M[c2];    }    for(i=0;i<k;i++)    {        for(j=0;j<k;j++)        {            if(num_Adam[i] < num_Eve[j])            {                mat[i][j] = 1;            }        }    }}bool find(int dir){    int i;    for(i=0;i<k;i++)    {        if(1 == mat[dir][i] && 0 == vis[i])        {            vis[i] = 1;            if(linky[i] == -1 || find(linky[i]))            {                linky[i] = dir;                linkx[dir] = i;                return true;            }        }    }    return false;}void Play(){    int i;    int ans = 0;    memset(linkx,-1,sizeof(linkx));    memset(linky,-1,sizeof(linky));    for(i=0;i<k;i++)    {        if(-1 == linkx[i])        {            memset(vis,0,sizeof(vis));            if(find(i))                ans++;        }    }    printf("%d\n",ans);}int main(){    int t;    scanf("%d",&t);    while(t--)    {        Init();        Play();    }    return 0;}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!