zoukankan      html  css  js  c++  java
  • 再续二分匹配

    最近又重新过了一遍二分匹配,发现了一些之前理解上的误区,这几天的纠结还是很值得的

    hdu1281 棋盘游戏

    //分别以行和列为单位,那么确定的和行和列唯一确定一个格子

    //行向列连边,求最多的可放置的格子,也就是求最大匹配

     //求重要点数,删除已匹配的边,判断是否存在最大匹配

    View Code
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N = 101;
    int n,m,my[N],mx[N];
    bool vis[N],mat[N][N];
    //分别以行和列为单位,那么确定的和行和列唯一确定一个格子
    //行向列连边,求最多的可放置的格子,也就是求最大匹配
    //求重要点数,删除已匹配的边,判断是否存在最大匹配
    int path(int s)
    {
    for(int i=0;i<m;i++)
    if(mat[s][i] && !vis[i])
    {
    vis[i]=1;
    if(my[i]==-1 || path(my[i]))
    {
    my[i]=s;
    mx[s]=i;
    return 1;
    }
    }
    return 0;
    }
    int match()
    {
    memset(mx,-1,sizeof(mx));
    memset(my,-1,sizeof(my));
    int ans=0;
    for(int i=0;i<n;i++)
    {
    memset(vis,false,sizeof(vis));
    ans+=path(i);
    }
    return ans;
    }
    int main()
    {
    int k,a,b,cas=0;
    while(scanf("%d %d %d",&n,&m,&k)==3)
    {
    memset(mat,false,sizeof(mat));
    for(int i=0;i<k;i++)
    {
    scanf("%d %d",&a,&b);
    a--,b--;
    mat[a][b]=1;
    }
    int ans=match();
    int maxx=0;
    int tmp[N];
    memcpy(tmp,mx,sizeof(mx));
    for(int i=0;i<n;i++)
    {
    if(tmp[i]==-1) continue;
    int y=tmp[i];
    mat[i][y]=false;
    int tmp=match();
    if(tmp<ans)
    maxx++;
    mat[i][y]=true;
    }
    printf("Board %d have %d important blanks for %d chessmen.\n",++cas,maxx,ans);
    }
    return 0;
    }


    hdu1507 Uncle Tom's Inherited Land*

    //很经典的模型,类似于用1*2的木板覆盖空格,很明显,每一个木板覆盖的一定是一个奇点和一个偶点

    //这就形成了一一对应的匹配关系,相邻的空格奇点向偶点连边,求最大匹配

    //关键是输出路径,注意判重

    View Code
    #include<iostream>
    #include<algorithm>
    #include<vector>
    using namespace std;
    const int N = 5050;
    int n,m,mx[N],my[N],hash1[110][110];
    bool mat[110][110],vis[N];
    int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
    struct point
    {
    int i,j;
    point(){}
    point(int a,int b):i(a),j(b){}
    };
    point xx[N],yy[N];
    vector<int> g[N];
    //很经典的模型,类似于用1*2的木板覆盖空格,很明显,每一个木板覆盖的一定是一个奇点和一个偶点
    //这就形成了一一对应的匹配关系,相邻的空格奇点向偶点连边,求最大匹配
    //关键是输出路径,注意判重
    int path(int s)
    {
    vector<int>::iterator it=g[s].begin();
    for(;it!=g[s].end();it++)
    {
    int v=*it;
    if(!vis[v])
    {
    vis[v]=true;
    if(my[v]==-1 || path(my[v]))
    {
    mx[s]=v;
    my[v]=s;
    return 1;
    }
    }
    }
    return 0;
    }
    int match()
    {
    int ans=0;
    memset(my,-1,sizeof(my));
    memset(mx,-1,sizeof(mx));
    for(int i=0;i<n;i++)
    {
    if(g[i].size()==0) continue;
    memset(vis,false,sizeof(vis));
    ans+=path(i);
    }
    return ans;
    }
    int main()
    {
    int nn,mm,k,a,b;
    while(scanf("%d %d",&nn,&mm)==2 && (nn||mm))
    {
    scanf("%d",&k);
    memset(mat,false,sizeof(mat));
    while(k--)
    {
    scanf("%d %d",&a,&b);
    a--,b--;
    mat[a][b]=true;
    }
    n=m=0;
    for(int i=0;i<nn;i++)
    for(int j=0;j<mm;j++)
    if(!mat[i][j] && ((i+j)&1))
    {
    xx[n]=point(i,j);
    hash1[i][j]=n++;//将每一个点重新标号,并且保持坐标
    }
    else if(!mat[i][j])
    {
    yy[m]=point(i,j);
    hash1[i][j]=m++;
    }
    for(int i=0;i<nn;i++)
    for(int j=0;j<mm;j++)
    if(!mat[i][j] && ((i+j)&1))//奇点向偶点连边
    {
    for(int t=0;t<4;t++)
    {
    int x=i+dir[t][0];
    int y=j+dir[t][1];
    if(x<0 || x>=nn || y<0 || y>=mm || mat[x][y])
    continue;
    g[hash1[i][j]].push_back(hash1[x][y]);
    }
    }
    int ans=match();
    printf("%d\n",ans);
    memset(vis,false,sizeof(vis));
    for(int i=0;i<n;i++)
    {
    if(mx[i]!=-1 && !vis[mx[i]])
    {
    int y=mx[i];
    vis[y]=true;//注意要判重
    printf("(%d,%d)--(%d,%d)\n",xx[i].i+1,xx[i].j+1,yy[y].i+1,yy[y].j+1);
    }
    }
    puts("");
    for(int i=0;i<n;i++)
    g[i].clear();
    }
    return 0;
    }

    hdu1498 50 years, 50 colors

    //每一个数字可以用其对应的行或列覆盖,那么对于每一个点,对应的行坐标向列坐标连边,

    //那么求变成求最小点覆盖了,选择最少的行或列覆盖所有的边,每一条边对应一个点;

    //当然,对应每一种数字,要单独判断

    View Code
    #include<iostream>

    #include<algorithm>

    #include<vector>

    using namespace std;

    const int N = 101;

    vector<int> g[N *N];

    int map[N][N], n, k, num[N], my[N];

    bool flag[55], vis[N];

    int path(int s)

    {

    vector<int>::iterator it = g[s].begin();

    for(; it != g[s].end(); it++)

    {

    int v = *it;

    if(!vis[v])

    {

    vis[v] = true;

    if(my[v] == -1 || path(my[v]))

    {

    my[v] = s;

    return 1;

    }

    }

    }

    return 0;

    }

    bool match(int u)

    {

    for(int i = 0; i < n; i++)

    for(int j = 0; j < n; j++)

    {

    if(map[i][j] == u)

    g[i].push_back(j);

    }

    int ans = 0;

    memset(my, -1, sizeof(my));

    for(int i = 0; i < n; i++)

    {

    if(g[i].size() == 0)continue;

    memset(vis, false, sizeof(vis));

    ans += path(i);

    }

    for(int i = 0; i < n; i++)

    g[i].clear();

    if(ans <= k)

    return true;

    return false;

    }

    void solve()

    {

    memset(flag, false, sizeof(flag));

    int t = 0, ans[51];

    for(int i = 1; i <= 50; i++)

    if(num[i])

    {

    if(!match(i))

    ans[t++] = i;

    }

    if(t == 0)

    {

    puts("-1");

    return ;

    }

    printf("%d", ans[0]);

    for(int i = 1; i < t; i++)

    printf(" %d", ans[i]);

    puts("");

    return ;

    }

    int main()

    {

    while(scanf("%d %d", &n, &k) == 2 && (n || k))

    {

    memset(num, 0, sizeof(num));

    for(int i = 0; i < n; i++)

    for(int j = 0; j < n; j++)

    {

    scanf("%d", &map[i][j]);

    num[map[i][j]]++;

    }

    solve();

    }

    return 0;
    }

    hdu3360 National Treasures

    //二分图,奇点文物的关键点只可能与偶点的文物冲突

    //注意,要先枚举所有文物再判断奇偶,保证奇点向偶点连边

    //最小覆盖,选最少的点覆盖所有冲突

    View Code
    #include<iostream>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int N = 1255;
    int mat[55][55],n,m,hash1[55][55],my[N];
    int dir[12][2] = {{-1, -2}, {-2, -1}, {-2, 1}, {-1, 2},
    {1, 2}, {2, 1}, {2, -1}, {1, -2},
    {-1, 0}, {0, 1}, {1, 0}, {0, -1} };
    bool vis[N];
    vector<int> g[N];
    //二分图,奇点文物的关键点只可能与偶点的文物冲突
    //注意,要先枚举所有文物再判断奇偶,保证奇点向偶点连边
    //最小覆盖,选最少的点覆盖所有冲突
    int path(int s)
    {
    vector<int>::iterator it=g[s].begin();
    for(;it!=g[s].end();it++)
    {
    int v=*it;
    if(!vis[v])
    {
    vis[v]=true;
    if(my[v]==-1 || path(my[v]))
    {
    my[v]=s;
    return 1;
    }
    }
    }
    return 0;
    }
    int match()
    {
    int ans=0;
    memset(my,-1,sizeof(my));
    for(int i=0;i<n;i++)
    {
    if(g[i].size()==0) continue;
    memset(vis,false,sizeof(vis));
    ans+=path(i);
    }
    return ans;
    }
    int main()
    {
    int r,c,cas=0;
    while(scanf("%d %d",&r,&c)==2 && (r||c))
    {
    n=m=0;
    for(int i=0;i<r;i++)
    for(int j=0;j<c;j++)
    {
    scanf("%d",&mat[i][j]);
    if(mat[i][j]!=-1)
    {
    if((i+j)&1)
    hash1[i][j]=n++;
    else hash1[i][j]=m++;
    }
    }
    for(int i=0;i<r;i++)
    for(int j=0;j<c;j++)
    {
    if(mat[i][j]==-1)
    continue;
    for(int k=0;k<12;k++)
    {
    if(!(mat[i][j]&(1<<k)))
    continue;
    int x=i+dir[k][0];
    int y=j+dir[k][1];
    if(x<0 || x>=r || y<0 || y>=c || mat[x][y]==-1)
    continue;
    if((i+j)&1)//这里跟之前的有所不同,因为二分图所有的边都是冲突的边,需要先枚举所有文物再判断奇偶点才能保证加入了所有的冲突边
    g[hash1[i][j]].push_back(hash1[x][y]);
    else g[hash1[x][y]].push_back(hash1[i][j]);
    }
    }
    int ans=match();
    printf("%d. %d\n",++cas,ans);
    for(int i=0;i<n;i++)
    g[i].clear();
    }
    return 0;
    }

     hdu2768 Cat vs. Dog

    题意:有n Cat 和m只Dog ,现在有k观众,每一个观众只能选择给一只Cat(或 Dog) 投支持票,给一只Dog(或Cat) 投反对票,只有当观众支持的Cat 或 Dog没有被淘汰时,该观众会继续观看。。。问最多有多少观众留下

    分析:一开始不知道怎么建图,,想着想着,突然想到最大权二分匹配了,也确实可以,每一个Cat i 和 Dog j 的匹配都有一个权值,结果悲剧的TLE,,,再想想,其实对象取错了,求的留下的观众,对象自然是观众了,,,如何寻找观众与观众的匹配??当然是寻找冲突的匹配,对于同一只Cat 或者Dog ,如果观众i 和 观众j 一个投支持票,而另一个投反对票,那么俩者冲突,建立匹配边,那么就变成了一个求最大的不与其他观众冲突的观众的个数了(最大独立点集== N (点数)- 最大匹配)

    通过这道题,还学到了一个知识点,就是sscanf, 用俩处理字符串还是听不错的

    View Code
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N = 500+10;
    int nx,ny,match[N];
    bool vis[N],mat[N][N];
    struct Vote
    {
        int l,h;
    }Cat[N],Dog[N];
    int path(int s)
    {
        for(int i=0;i<ny;i++)
        {
            if(mat[s][i] && !vis[i])
            {
                vis[i]=true;
                if(match[i]==-1 || path(match[i]))
                {
                    match[i]=s;
                    return 1;
                }
            }
        }
        return 0;
    }
    int main()
    {
        char str1[20],str2[20];
        int T,k,a,b,n,m;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d %d %d",&n,&m,&k);
            nx=ny=0;
            for(int i=0;i<k;i++)
            {
                scanf("%s %s",str1,str2);
                sscanf(str1,"%*c%d",&a);//%*c 表示滤掉第一个匹配的字符
                sscanf(str2,"%*c%d",&b);
                if(str1[0]=='C')
                {
                    Cat[nx].l=a;
                    Cat[nx++].h=b;
                }
                else {
                    Dog[ny].l=a;
                    Dog[ny++].h=b;
                }
            }
            memset(mat,false,sizeof(mat));
            for(int i=0;i<nx;i++)
                for(int j=0;j<ny;j++)
                {
                    if(Cat[i].l==Dog[j].h || Cat[i].h==Dog[j].l)
                        mat[i][j]=true;
                }
            memset(match,-1,sizeof(match));
            int ans=0;
            for(int i=0;i<nx;i++)
            {
                memset(vis,false,sizeof(vis));
                ans+=path(i);
            }
            printf("%d\n",k-ans);
        }
        return 0;
    }
  • 相关阅读:
    Android创建上下文(appContext)
    java泛型——同一类型
    (转)cglib介绍与原理——CallBackFilter、延迟加载
    cglib常用api
    ES6——Proxy实现Web服务,进行方法拦截,通过方法名称生成网址
    day4幸运抽奖系统更新
    day7_abstractClass_interface
    day6_inheritance_polymorphis
    day5_package
    day4_class_method_array
  • 原文地址:https://www.cnblogs.com/nanke/p/2429711.html
Copyright © 2011-2022 走看看