zoukankan      html  css  js  c++  java
  • [kuangbin带你飞]专题十 匹配问题

     
     
    A-L 二分匹配 M-O 二分图多重匹配 P-Q 二分图最大权匹配 R-S 一般图匹配带花树 模板请自己找
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     


    61 / 72 Problem A HDU 1045 Fire Net

    s.二分匹配或者暴搜

    52 / 112 Problem B HDU 2444 The Accomodation of Students

    d.有一些学生,他们之间有些互相认识,有些不认识,其中若A和B认识,B和C认识,并不代表A和C认识,现在提供互相认识的学生编号,问是否能将这些学生分成两组,同一组的学生互相不认识,如果可以,则分配给这些学生双人间,只有是朋友才能住双人间,问最多要准备几间房

    即判断是不是二分图,若是,其最大匹配数是多少?

    s.首先用交叉染色法判断是不是二分图,然后用匈牙利算法求最大匹配。

    c.唯一比较坑的是边数开了1w,提交G++超时!而用C++却Runtime Error,才知道可能是数组原因。。

    #include<iostream>
    #include<stdio.h>
    #include<queue>
    #include<string.h>
    using namespace std;
    
    #define MAXN 205//点数
    #define MAXM 41000//边数。不要吝啬内存,只要不超限。一定要开够
    
    int color[MAXN];
    int n;
    
    struct Edge{
        int to,next;
    }edge[MAXM];
    
    int head[MAXN];
    int tot;
    
    void addedge(int u,int v){
        edge[tot].to=v;
        edge[tot].next=head[u];
        head[u]=tot++;
    }
    
    void init(){
        tot=0;
        memset(head,-1,sizeof(head));
    }
    
    bool bfs(int start){//bfs交叉染色,两种颜色标记为 1 和 -1,未染色标记为 0
        int v;
        queue<int>q;
        color[start]=1;
        q.push(start);
    
        while(!q.empty()){
            int temp=q.front();
            q.pop();
    
            for(int i=head[temp];i!=-1;i=edge[i].next){//
    
                v=edge[i].to;
    
                if(color[v]==0){//未染色
                    if(color[temp]==1){
                        color[v]=-1;
                    }
                    else{
                        color[v]=1;
                    }
                    q.push(v);
                }
                else{//已染色
                    if(color[v]==color[temp]){//相邻的两点颜色相同
                        return false;//不能交叉染色
                    }
                }
    
            }
        }
        return true;
    }
    
    //匈牙利算法
    int linker[MAXN];
    bool used[MAXN];
    
    bool dfs(int u){
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].to;
            if(!used[v]){
                used[v]=true;
                if(linker[v]==-1||dfs(linker[v])){
                    linker[v]=u;
                    return true;
                }
            }
        }
        return false;
    }
    int hungary(){
        int res=0;
        memset(linker,-1,sizeof(linker));
        //点的编号0~uN-1
        for(int u=0;u<n;u++){
            if(color[u]==-1)continue;//只看颜色是1的集合
    
            memset(used,false,sizeof(used));
            if(dfs(u))res++;
        }
        return res;
    }
    
    int main(){
    
        int m;
        int u,v;
        bool flag;
    
        while(~scanf("%d",&n)){
    
            memset(color,0,sizeof(color));
            init();
    
            scanf("%d",&m);
    
            for(int i=0;i<m;++i){
                scanf("%d%d",&u,&v);
    
                addedge(u-1,v-1);
                addedge(v-1,u-1);
            }
    
            flag=true;
    
            for(int i=0;i<n;++i){
                if(color[i]!=0)continue;//已经染色
                if(!bfs(i)){//不是二分图
                    flag=false;
                    break;
                }
            }
    
            if(flag){
                printf("%d
    ",hungary());
            }
            else{
                printf("No
    ");
            }
        }
    
        return 0;
    }
    View Code

    c2.这里的程序hungary()用vector实现,同时染色用的dfs

    /*
    HDU 2444 The Accomodation of Students
    
    */
    #include<iostream>
    #include<string.h>
    #include<vector>
    using namespace std;
    #define MAXN 202
    vector<int>EV[MAXN];
    int linker[MAXN];
    bool used[MAXN];
    int uN;
    int matchs[MAXN],cnt[MAXN];
    bool dfs(int u)
    {
        int i;
        for(i=0;i<EV[u].size();i++)
        {
            int v=EV[u][i];
            if(!used[v])
            {
                used[v]=true;
                if(linker[v]==-1||dfs(linker[v]))
                {
                    linker[v]=u;
                    return true;
                }    
            }    
        }
        return false;    
    }   
    int hungary()
    {
        int res=0;
        int u;
        memset(linker,-1,sizeof(linker));
        for(u=1;u<=uN;u++)
        {
            memset(used,false,sizeof(used));
            if(dfs(u))  res++;
        }
        return res;    
    }  
    bool judge(int x,int y)
    {
        int i;
        for(i=0;i<EV[x].size();i++)
        {
            if(cnt[EV[x][i]]==0)
            {
                cnt[EV[x][i]]=-1*y;
                matchs[EV[x][i]]=true;
                if(!judge(EV[x][i],-1*y)) return false;
            }   
            else if(cnt[EV[x][i]]==y)  return false; 
        }    
        return true;
    }    
    bool matched()
    {
        int i;
        memset(matchs,false,sizeof(matchs));
        for(i=1;i<=uN;i++)
        {
            if(EV[i].size()&&!matchs[i])
            {
                memset(cnt,0,sizeof(cnt));
                cnt[i]=-1;
                matchs[i]=true;
                if(!judge(i,-1)) return false;
            }    
        }
        return true;    
    }     
    int main()
    {
        int m;
        int i;
        int u,v;
        while(scanf("%d%d",&uN,&m)!=EOF)
        {
            for(i=1;i<=uN;i++)
              if(EV[i].size())  EV[i].clear();
            while(m--)
            {
                scanf("%d%d",&u,&v);
                EV[u].push_back(v);
                EV[v].push_back(u);
            }    
         
             if(matched())
                      printf("%d
    ",hungary()/2);
             else  printf("No
    ");    
        } 
        return 0;
    }
    View Code

    45 / 86 Problem C HDU 1083 Courses

    s.最大匹配


    44 / 63 Problem D HDU 1281 棋盘游戏

    s.二分匹配


    35 / 85 Problem E HDU 2819 Swap

    s.二分匹配,记录过程


    29 / 110 Problem F HDU 2389 Rain on your Parade

    s.先用匈牙利算法的DFS版本,一直超时,

    后改用Hopcroft-Carp算法,顺利AC。


    29 / 77 Problem G HDU 4185 Oil Skimming

    s.二分匹配


    27 / 39 Problem H POJ 3020 Antenna Placement

    s.二分匹配,主要是建图


    28 / 44 Problem I HDU 1054 Strategic Game

    此题就是二分图的最小点覆盖。
    点比较多,1500个。普通的匈牙利算法会超时的。可以用邻接表实现匈牙利算法。
    但是我启用二分匹配中的战斗机:Hopcroft-Carp的算法。专门处理点比较多的情况。
     
    好像也可以用树形DP做。等下来学习下。


    25 / 36 Problem J HDU 1151 Air Raid

    d.一个有向无环图(DAG),M个点,K条有向边,求DAG的最小路径覆盖数

    s.DAG的最小路径覆盖数=DAG图中的点数-相应二分图中的最大匹配数

    /*
    顶点编号从0开始的
    邻接矩阵(匈牙利算法)
    二分图匹配(匈牙利算法的DFS实现)(邻接矩阵形式)
    初始化:g[][]两边顶点的划分情况
    建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配
    g没有边相连则初始化为0
    uN是匹配左边的顶点数,vN是匹配右边的顶点数
    左边是X集,右边是Y集
    调用:res=hungary();输出最大匹配数
    优点:适用于稠密图,DFS找增广路,实现简洁易于理解
    时间复杂度:O(VE)
    */
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    
    const int MAXN=512;
    int uN,vN;//u,v 的数目,使用前面必须赋值
    int g[MAXN][MAXN];//邻接矩阵,记得初始化
    int linker[MAXN];//linker[v]=u,表示v(右边Y集合中的点)连接到u(左边X集合中的点)
    bool used[MAXN];
    bool dfs(int u){//判断以X集合中的节点u为起点的增广路径是否存在
        for(int v=0;v<vN;v++)//枚举右边Y集合中的点
            if(g[u][v]&&!used[v]){//搜索Y集合中所有与u相连的未访问点v
                used[v]=true;//访问节点v
                if(linker[v]==-1||dfs(linker[v])){//是否存在增广路径
                    //若v是未盖点(linker[v]==-1表示没有与v相连的点,即v是未盖点),找到增广路径
                    //或者存在从与v相连的匹配点linker[v]出发的增广路径
                    linker[v]=u;//设定(u,v)为匹配边,v连接到u
                    return true;//返回找到增广路径
                }
            }
            return false;
    }
    int hungary(){//返回最大匹配数(即最多的匹配边的条数)
        int res=0;//最大匹配数
        memset(linker,-1,sizeof(linker));//匹配边集初始化为空
        for(int u=0;u<uN;u++){//找X集合中的点的增广路
            memset(used,false,sizeof(used));//设Y集合中的所有节点的未访问标志
            if(dfs(u))res++;//找到增广路,匹配数(即匹配边的条数)+1
        }
        return res;
    }
    
    int main(){
        int i,ans;
        int K,M;
        int u,v;
        int T;
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&M,&K);
            uN=M;//匹配左边的顶点数
            vN=M;//匹配右边的顶点数
            memset(g,0,sizeof(g));//二分图的邻接矩阵初始化
            for(i=0;i<K;++i){
                scanf("%d%d",&u,&v);
                g[--u][--v]=1;//顶点编号从0开始的
            }
            ans=M-hungary();
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    26 / 64 Problem K POJ 2594 Treasure Exploration

    此题跟普通的路径覆盖有点不同。
    You should notice that the roads of two different robots may contain some same point.
    也就是不同的路径可以有重复点。
    先用floyed求闭包,就是把间接相连的点也连起来。


    26 / 61 Problem L HDU 3829 Cat VS Dog

    最大独立集


    23 / 56 Problem M POJ 2289 Jamie's Contact Groups

    多重匹配+二分

    貌似也可以用最大流+二分做

    d.Jamie有很多联系人,但是很不方便管理,他想把这些联系人分成组,已知这些联系人可以被分到哪个组中去,而且要求每个组的联系人上限最小,即有一整数k,使每个组的联系人数都不大于k,问这个k最小是多少?

    s.一对多的二分图的多重匹配。二分图的多重匹配算法的实现类似于匈牙利算法,对于集合x中的元素xi,找到一个与其相连的元素yi后,检查匈牙利算法的两个条件是否成立,若yi未被匹配,则将

    xi,yi匹配。否则,如果与yi匹配的元素已经达到上限,那么在所有与yi匹配的元素中选择一个元素,检查是否能找到一条增广路径,如果能,则让出位置,让xi与yi匹配。

    二分求出limit,知道找到可以构成多重匹配的最小限制limit,在main函数中二分搜索。

    #include<stdio.h>
    #include<string.h>
    #include<queue>
    #include<algorithm>
    using namespace std;
    #define N 1010
    int vis[N], maps[N][N], ans, n, m;
    struct node
    {
        int cnt;///和yi相匹配的个数;
        int k[N];///和yi相匹配的x的集合;
    }Linky[N];
    
    bool Find(int u, int limit)
    {
        for(int i=1; i<=m; i++)
        {
            if(!vis[i] && maps[u][i])
            {
                vis[i] = 1;
                if(Linky[i].cnt < limit)
                {
                    Linky[i].k[ Linky[i].cnt++ ] = u;
                    return true;
                }
                for(int j=0; j<Linky[i].cnt; j++)
                {
                    if(Find( Linky[i].k[j], limit ))
                    {
                        Linky[i].k[j] = u;
                        return true;
                    }
                }
            }
        }
        return false;
    }
    
    bool xyl(int limit)///匈牙利算法;
    {
        memset(Linky, 0, sizeof(Linky));
        for(int i=1; i<=n; i++)
        {
            memset(vis, 0, sizeof(vis));
            if(!Find(i, limit))///当前的limit让i没有匹配,所以不能用limit;
                return false;
        }
        return true;
    }
    
    int main()
    {
        int x;
        char s[20], ch;
        while(scanf("%d %d", &n, &m), m+n)
        {
            memset(maps, 0, sizeof(maps));
            for(int i=1; i<=n; i++)
            {
                scanf("%s", s);
                while(1)
                {
                    scanf("%d%c", &x, &ch);
                    maps[i][x+1] = 1;
                    if(ch == '
    ')
                        break;
                }
            }
            int L = 1, R = n;
            ans = n;
            while(L <= R)
            {
                int mid = (L+R)/2;
                if(xyl(mid))///如果当前mid满足题意;
                {
                    R = mid-1;
                    ans = mid;
                }
                else
                    L = mid+1;
            }
            printf("%d
    ", ans);
        }
        return 0;
    }
    View Code

    15 / 74 Problem N POJ 2112 Optimal Milking

    floyd+二分+最大流

    题目描述:
    农场主John将他的K(1≤K≤30)个挤奶器运到牧场,在那里有C(1≤C≤200)头奶牛,在奶
    牛和挤奶器之间有一组不同长度的路。K个挤奶器的位置用1~K的编号标明,奶牛的位置用K+1~
    K+C的编号标明。
    每台挤奶器每天最多能为M(1≤M≤15)头奶牛挤奶。
    编写程序,寻找一个方案,安排每头奶牛到某个挤奶器挤奶,并使得C头奶牛需要走的所有
    路程中的最大路程最小。每个测试数据中至少有一个安排方案。每条奶牛到挤奶器有多条路。
    // 本题的求解算法:先用Floyd算法求出能达到的任意两点之间的最短路径,然后用Dinic算法
    // 求最大流;搜索最大距离的最小值采用二分法进行。

    // 建图问题 : 给个源点 s 。源点到奶牛的流量为1 如果奶牛到达挤奶器距离在max_min范围内 那没就然 该奶牛到该挤奶器的流量为1
    // 再给个汇点 那么挤奶器到汇点的流量就是M 正好符合限制
    /// 然后二分查找 max_min就可以了

    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    using namespace std;
    #define MOD 1000000007
    #define INF 1000000000
    #define maxn 310
    #define maxm 48010
    #define LL  __int64//long long
    int M,K,C,N;
    int cap[maxn][maxn],flow[maxn][maxn];
    int dis[maxn][maxn];//,bl[1100];
    void build(int max_min){  // 建图
        int i,j;
        memset(cap,0,sizeof(cap));
        memset(flow,0,sizeof(flow));
       for(i=1;i<=K;i++) cap[i][N+1]=M;
       for(i=K+1;i<=N;i++) cap[0][i]=1;
       for(i=K+1;i<=N;i++)
        for(j=1;j<=K;j++)
         if(dis[i][j]<=max_min) cap[i][j]=1;
    }
    int level[maxn];
    bool BFS(int s,int t){
       memset(level,0,sizeof(level));
       queue<int> Q;
       int u;
       int i;
       Q.push(s);
       level[s]=1;
       while(!Q.empty()){
          u=Q.front();
          Q.pop();
          if(u==t) return true;
          for(i=1;i<=t;i++)
            if(!level[i]&&cap[u][i]>flow[u][i])
            {
                level[i]=level[u]+1;
                Q.push(i);
            }
       }
       return false;
    }
    int dfs(int u,int maxf,int t){
        if(u==t||maxf==0) return maxf;
        int ret=0,f,i;
        for(i=1;i<=t;i++)
          if(cap[u][i]>flow[u][i]&&level[u]+1==level[i]){
               f= dfs(i,min(maxf,cap[u][i]-flow[u][i]),t);
               flow[u][i]+=f;
               flow[i][u]-=f;
               maxf-=f;
               ret+=f;
               if(maxf==0) break;
        }
        return ret;
    }
    int Dinic(int s,int t){
       int flow=0;
       while(BFS(s,t)){
        flow+=dfs(s,INF,t);
       }
       return flow;
    }
    void init(){
       // memset(cap,0,sizeof(cap));
      //  memset(flow,0,sizeof(flow));
    }
    int main(){
        int i,j,k;
       while(scanf("%d %d %d",&K,&C,&M)!=EOF){
    
        N=K+C;
        for(i=1;i<=N;i++)
           for(j=1;j<=N;j++){
            scanf("%d",&dis[i][j]);
            if(dis[i][j]==0) dis[i][j]=INF;
        }
        for(k=1;k<=N;k++)
           for(i=1;i<=N;i++)
             for(j=1;j<=N;j++)
                if(dis[i][j]>dis[i][k]+dis[k][j])
                  dis[i][j]=dis[i][k]+dis[k][j];
        /* for(i=0;i<=N+1;i++,printf("
    "))
            for(k=0;k<=N+1;k++)
             printf("%d ",cap[i][k]);*/
          int L=0,R=100000,mid;
          int tp;
          while(L<R){// 二分枚举 max_min
             mid=(L+R)>>1;
             build(mid);
             tp=Dinic(0,N+1);
             if(tp>=C) R=mid;
             else L=mid+1;
          }
         printf("%d
    ",R);
       }
    
       return 0;
    }
    View Code

    14 / 31 Problem O POJ 3189 Steady Cow Assignment

    二分图多重匹配/网络流

    题目描述:有n头奶牛,m个棚,每个奶牛对每个棚都有一个喜爱程度。当然啦,棚子也是有脾气的,并不是奶牛想住进来就住进来,

    超出棚子的最大容量了,棚子是拒绝的。现在要给每个奶牛安家,本宝宝是一个公正无私的人类,

    所以要找一个奶牛喜爱程度差值最小的方案(虽然这样大家的喜爱程度可能普遍偏低,因为绝对公平并不代表合理啊),问喜爱程度的区间最小为多大?

    解题思路:每个棚子并不是住一个奶牛,所以是多重匹配咯。匹配的时候二分枚举喜爱程度的区间大小,根据区间大小来枚举区间的起点和终点,

    然后跑多重匹配判断是否合法即可。

    数据读入的时候maps[i][j]并不是i_th奶牛对j_th棚子的喜爱值,而是i_th奶牛对maps[i][j]棚子的喜爱程度为j。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int maxn = 1010;
    int maps[maxn][22], vis[22], used[22][maxn];
    int link[22], limit[22], n, m, s, e, mid;
    bool Find (int u)
    {
        for (int i=1; i<=m; i++)
        {//多重匹配
            if (!vis[i] && maps[u][i]<e && s<=maps[u][i])
            {
                vis[i] = 1;
                if (link[i] < limit[i])
                {//i棚子未满
                    used[i][link[i] ++] = u;
                    return true;
                }
                for (int j=0; j<limit[i]; j++)//i棚子已满,寻找增广路
                    if (Find(used[i][j]))
                    {
                        used[i][j] = u;
                        return true;
                    }
            }
        }
        return false;
    }
    bool hungry ()
    {
        for (s=1; s<=m+1-mid; s++)
        {//枚举区间起点与终点
            int ans = 0;
            e = s + mid;
            memset (link, 0, sizeof(link));
            for (int i=1; i<=n; i++)
            {
                memset (vis, 0, sizeof(vis));
                if (Find (i))
                    ans ++;
            }
            if (ans == n)
                return true;
        }
        return false;
    }
    int main ()
    {
        while (scanf ("%d %d", &n, &m) != EOF)
        {
            for (int i=1; i<=n; i++)
                for (int j=1; j<=m; j++)
                {
                    scanf ("%d", &mid);
                    maps[i][mid] = j;
                }
            for (int i=1; i<=m; i++)
                scanf ("%d", &limit[i]);
            int left = 1, right = m, ans = 0;
            while (left<=right)
            {//二分枚举区间
                mid = (right+left)/2;
                if (hungry())
                    {
                        ans = mid;
                        right = mid - 1;
                    }
                else
                    left = mid + 1;
            }
            printf ("%d
    ", ans);
        }
        return 0;
    }
    View Code

    法2.首先二分图匹配,正常情况下,都是单个单个的点这样匹配、
    但是像这道题,右边的点一个可以匹配好几个左边的点。也是用匈牙利算法,不过要做少少修改。
    枚举答案的时候,有两种方法:
    一个是移动区间的方法。复杂度O(B)。注意,只移动区间右边的时候不用重新匹配,只需要接着上次没匹配完的继续匹配就行了
    一个是二分法。复杂度 O(B lgB)
    由于数据的问题,这两种方法时间相差无几。在 16ms 和 32ms 之间。

    ps:这里是枚举棚子,与上面不同

    #include <stdio.h>
    
    struct barn {
        int link[1024], cnt, vis, cap;
    };
    struct cow {
        int rank[32];
    };
    
    struct barn barns[32];
    struct cow cows[1024];
    int start, end, last_pos;
    int N, B, tm;
    
    int dfs(int c)
    {
        int i, j;
        struct barn *b;
    
        for (i = start; i <= end; i++) {
            b = &barns[cows[c].rank[i]];
            if (b->vis == tm)
                continue;
            b->vis = tm;
            if (b->cnt < b->cap) {
                b->link[b->cnt++] = c;
                return 1;
            }
            for (j = 0; j < b->cap; j++)
                if (dfs(b->link[j])) {
                    b->link[j] = c;
                    return 1;
                }
        }
        return 0;
    }
    
    inline void init()
    {
        int i;
    
        for (i = 1; i <= B; i++)
            barns[i].cnt = 0;
        last_pos = 1;
    }
    
    inline int match()
    {
        int i, j;
    
        for (i = last_pos; i <= N; i++) {
            tm++;
            if (!dfs(i))
                break;
        }
        last_pos = i;
        return i > N;
    }
    
    int main()
    {
        int i, j, ans;
    
        //freopen("e:\test\in.txt", "r", stdin);
    
        scanf("%d%d", &N, &B);
        for (i = 1; i <= N; i++)
            for (j = 1; j <= B; j++)
                scanf("%d", &cows[i].rank[j]);
        for (i = 1; i <= B; i++)
            scanf("%d", &barns[i].cap);
    
    #if 1
        // O(B)
        ans = B;
        start = end = 1;
        while (start <= B && end <= B) {
            init();
            while (end <= B && !match())
                end++;
            if (end - start + 1 < ans)
                ans = end - start + 1;
            start++;
        }
    #else
        // O(B*lgB)
        int l, r, m;
    
        l = 1;
        r = B;
        while (l <= r) {
            m = (l + r) / 2;
            for (start = 1; start <= B - m + 1; start++) {
                end = start + m - 1;
                init();
                if (match())
                    break;
            }
            if (start == B - m + 2) {
                // failed
                l = m + 1;
            } else
                r = m - 1;
        }
        ans = r + 1;
    #endif
    
        printf("%d
    ", ans);
    
        return 0;
    }
    View Code

    29 / 56 Problem P HDU 2255 奔小康赚大钱

    KM算法 模板题

     
    里面有解释。
     
    网上也好多KM算法的讲解。
     
    不管了,会用就行了,套模板
    #include <stdio.h>
    #include <algorithm>
    #include <string.h>
    #include <iostream>
    using namespace std;
    
    /*  KM算法
     *   复杂度O(nx*nx*ny)
     *  求最大权匹配
     *   若求最小权匹配,可将权值取相反数,结果取相反数
     *  点的编号从0开始
     */
    const int N = 310;
    const int INF = 0x3f3f3f3f;
    int nx,ny;//两边的点数
    int g[N][N];//二分图描述
    int linker[N],lx[N],ly[N];//y中各点匹配状态,x,y中的点标号
    int slack[N];
    bool visx[N],visy[N];
    
    bool DFS(int x)
    {
        visx[x] = true;
        for(int y = 0; y < ny; y++)
        {
            if(visy[y])continue;
            int tmp = lx[x] + ly[y] - g[x][y];
            if(tmp == 0)
            {
                visy[y] = true;
                if(linker[y] == -1 || DFS(linker[y]))
                {
                    linker[y] = x;
                    return true;
                }
            }
            else if(slack[y] > tmp)
                slack[y] = tmp;
        }
        return false;
    }
    int KM()
    {
        memset(linker,-1,sizeof(linker));
        memset(ly,0,sizeof(ly));
        for(int i = 0;i < nx;i++)
        {
            lx[i] = -INF;
            for(int j = 0;j < ny;j++)
                if(g[i][j] > lx[i])
                    lx[i] = g[i][j];
        }
        for(int x = 0;x < nx;x++)
        {
            for(int i = 0;i < ny;i++)
                slack[i] = INF;
            while(true)
            {
                memset(visx,false,sizeof(visx));
                memset(visy,false,sizeof(visy));
                if(DFS(x))break;
                int d = INF;
                for(int i = 0;i < ny;i++)
                    if(!visy[i] && d > slack[i])
                        d = slack[i];
                for(int i = 0;i < nx;i++)
                    if(visx[i])
                        lx[i] -= d;
                for(int i = 0;i < ny;i++)
                {
                    if(visy[i])ly[i] += d;
                    else slack[i] -= d;
                }
            }
        }
        int res = 0;
        for(int i = 0;i < ny;i++)
            if(linker[i] != -1)
                res += g[linker[i]][i];
        return res;
    }
    //HDU 2255
    int main()
    {
        int n;
        while(scanf("%d",&n) == 1)
        {
            for(int i = 0;i < n;i++)
                for(int j = 0;j < n;j++)
                    scanf("%d",&g[i][j]);
            nx = ny = n;
            printf("%d
    ",KM());
        }
        return 0;
    }
    View Code

    20 / 33 Problem Q HDU 3488 Tour

    KM

    好像费用流也可以

    题意:这题自己YY了下,没想到结论还是对的。题目告诉我们一个有向图,现在问将图中的每一个点都划分到一个环中的最少代价是多少?每条边都有一个代价。

    解法:由于要成环,那么将这个图进行拆点,就变成了单向的二分图了,此时一个完备匹配就是一种连线策略,只要保证没有边是和自己相连,就能够满足题目中要求的每个点至少属于一个环。证明也是很简单的。因为我们总可以从一个完备匹配中找出起点,然后再从匹配点作为起点找......

    左图可以看做是1,2成环,3,4,5成环。

    代码如下:

    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    int N, M;
    
    const int INF = 0x3f3f3f3f;
    int w[205][205];
    int lx[205], ly[205];
    int sx[205], sy[205];
    int match[205], slack[205];
    
    int path(int u) {
        sx[u] = 1;
        for (int i = 1; i <= N; ++i) {
            if (sy[i]) continue;
            int t = lx[u] + ly[i] - w[u][i];
            if (!t) {
                sy[i] = 1;
                if (!match[i] || path(match[i])) {
                    match[i] = u;
                    return true;
                }
            } else {
                slack[i] = min(slack[i], t);
            }
        }
        return false;
    }
    
    void KM() {
        memset(match, 0, sizeof (match));
        memset(lx, 0x80, sizeof (lx));
        memset(ly, 0, sizeof (ly));
        for (int i = 1; i <= N; ++i) {
            for (int j = 1; j <= N; ++j) { 
                lx[i] = max(lx[i], w[i][j]);
            }
        }
        for (int i = 1; i <= N; ++i) {
            memset(slack, 0x3f, sizeof (slack));
            while (1) {
                memset(sx, 0, sizeof (sx));
                memset(sy, 0, sizeof (sy));
                if (path(i)) break;
                int d = INF;
                for (int j = 1; j <= N; ++j) {
                    if (!sy[j]) d = min(d, slack[j]);
                }
                for (int j = 1; j <= N; ++j) {
                    if (sx[j]) lx[j] -= d;
                    if (sy[j]) ly[j] += d;
                    else slack[j] -= d;
                }
            }
        }
        int ret = 0;
        for (int i = 1; i <= N; ++i) {
            ret += w[match[i]][i];
        }
        printf("%d
    ", -ret);
    }
    
    int main() {
        int T, x, y, ct;
        scanf("%d", &T);
        while (T--) {
            scanf("%d %d", &N, &M);
            memset(w, 0x80, sizeof (w));
            for (int i = 1; i <= M; ++i) {
                scanf("%d %d %d", &x, &y, &ct);
                w[x][y] = max(w[x][y], -ct);
            }
            KM();
        }
        return 0;    
    }
    View Code

    10 / 20 Problem R URAL 1099 Work Scheduling

    一般图匹配带花树

    模板题!!!

    以下是模板:

    /* ***********************************************
    Author        :kuangbin
    Created Time  :2013/8/21 22:56:05
    File Name     :F:2013ACM练习专题学习图论一般图匹配带花树URAL1099.cpp
    ************************************************ */
    
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <set>
    #include <map>
    #include <string>
    #include <math.h>
    #include <stdlib.h>
    #include <time.h>
    using namespace std;
    
    const int MAXN = 250;
    int N; //点的个数,点的编号从1到N
    bool Graph[MAXN][MAXN];
    int Match[MAXN];
    bool InQueue[MAXN],InPath[MAXN],InBlossom[MAXN];
    int Head,Tail;
    int Queue[MAXN];
    int Start,Finish;
    int NewBase;
    int Father[MAXN],Base[MAXN];
    int Count;//匹配数,匹配对数是Count/2
    void CreateGraph()
    {
        int u,v;
        memset(Graph,false,sizeof(Graph));
        scanf("%d",&N);
        while(scanf("%d%d",&u,&v) == 2)
        {
            Graph[u][v] = Graph[v][u] = true;
        }
    }
    void Push(int u)
    {
        Queue[Tail] = u;
        Tail++;
        InQueue[u] = true;
    }
    int Pop()
    {
        int res = Queue[Head];
        Head++;
        return res;
    }
    int FindCommonAncestor(int u,int v)
    {
        memset(InPath,false,sizeof(InPath));
        while(true)
        {
            u = Base[u];
            InPath[u] = true;
            if(u == Start) break;
            u = Father[Match[u]];
        }
        while(true)
        {
            v = Base[v];
            if(InPath[v])break;
            v = Father[Match[v]];
        }
        return v;
    }
    void ResetTrace(int u)
    {
        int v;
        while(Base[u] != NewBase)
        {
            v = Match[u];
            InBlossom[Base[u]] = InBlossom[Base[v]] = true;
            u = Father[v];
            if(Base[u] != NewBase) Father[u] = v;
        }
    }
    void BloosomContract(int u,int v)
    {
        NewBase = FindCommonAncestor(u,v);
        memset(InBlossom,false,sizeof(InBlossom));
        ResetTrace(u);
        ResetTrace(v);
        if(Base[u] != NewBase) Father[u] = v;
        if(Base[v] != NewBase) Father[v] = u;
        for(int tu = 1; tu <= N; tu++)
            if(InBlossom[Base[tu]])
            {
                Base[tu] = NewBase;
                if(!InQueue[tu]) Push(tu);
            }
    }
    void FindAugmentingPath()
    {
        memset(InQueue,false,sizeof(InQueue));
        memset(Father,0,sizeof(Father));
        for(int i = 1;i <= N;i++)
            Base[i] = i;
        Head = Tail = 1;
        Push(Start);
        Finish = 0;
        while(Head < Tail)
        {
            int u = Pop();
            for(int v = 1; v <= N; v++)
                if(Graph[u][v] && (Base[u] != Base[v]) && (Match[u] != v))
                {
                    if((v == Start) || ((Match[v] > 0) && Father[Match[v]] > 0))
                        BloosomContract(u,v);
                    else if(Father[v] == 0)
                    {
                        Father[v] = u;
                        if(Match[v] > 0)
                            Push(Match[v]);
                        else
                        {
                            Finish = v;
                            return;
                        }
                    }
                }
        }
    }
    void AugmentPath()
    {
        int u,v,w;
        u = Finish;
        while(u > 0)
        {
            v = Father[u];
            w = Match[v];
            Match[v] = u;
            Match[u] = v;
            u = w;
        }
    }
    void Edmonds()
    {
        memset(Match,0,sizeof(Match));
        for(int u = 1; u <= N; u++)
            if(Match[u] == 0)
            {
                Start = u;
                FindAugmentingPath();
                if(Finish > 0)AugmentPath();
            }
    }
    void PrintMatch()
    {
        Count = 0;
        for(int u = 1; u <= N;u++)
            if(Match[u] > 0)
                Count++;
        printf("%d
    ",Count);
        for(int u = 1; u <= N; u++)
            if(u < Match[u])
                printf("%d %d
    ",u,Match[u]);
    }
    int main()
    {
        CreateGraph();//建图
        Edmonds();//进行匹配
        PrintMatch();//输出匹配数和匹配
        return 0;
    }
    View Code

    7 / 22 Problem S HDU 4687 Boke and Tsukkomi

    一般图匹配带花树

    此题求哪些边在任何一般图极大匹配中都无用,对于任意一条边i,设i的两个端点分别为si,ti,

    则任意一个极大匹配中都必然有si或ti至少一个点被匹配,当在图中去掉si,ti两个点时,匹配数会损失一个或两个.

    如果损失两个,就说明在极大匹配中这两个点分别连接不同的边,于是边i是无用的

    所以总体思路:一般图匹配求出最大匹配数cnt0,分别试着去掉每条边的端点,再次匹配,匹配数如果小于cnt0-1,则这条边无用,记录

    /* ***********************************************
    Author        :kuangbin
    Created Time  :2013/8/23 19:28:08
    File Name     :F:2013ACM练习专题学习图论一般图匹配带花树HDU4687.cpp
    ************************************************ */
    
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <set>
    #include <map>
    #include <string>
    #include <math.h>
    #include <stdlib.h>
    #include <time.h>
    using namespace std;
    const int MAXN = 50;
    int N; //点的个数,点的编号从1到N
    bool Graph[MAXN][MAXN];
    int Match[MAXN];
    bool InQueue[MAXN],InPath[MAXN],InBlossom[MAXN];
    int Head,Tail;
    int Queue[MAXN];
    int Start,Finish;
    int NewBase;
    int Father[MAXN],Base[MAXN];
    int Count;
    void Push(int u)
    {
        Queue[Tail] = u;
        Tail++;
        InQueue[u] = true;
    }
    int Pop()
    {
        int res = Queue[Head];
        Head++;
        return res;
    }
    int FindCommonAncestor(int u,int v)
    {
        memset(InPath,false,sizeof(InPath));
        while(true)
        {
            u = Base[u];
            InPath[u] = true;
            if(u == Start) break;
            u = Father[Match[u]];
        }
        while(true)
        {
            v = Base[v];
            if(InPath[v])break;
            v = Father[Match[v]];
        }
        return v;
    }
    void ResetTrace(int u)
    {
        int v;
        while(Base[u] != NewBase)
        {
            v = Match[u];
            InBlossom[Base[u]] = InBlossom[Base[v]] = true;
            u = Father[v];
            if(Base[u] != NewBase) Father[u] = v;
        }
    }
    void BloosomContract(int u,int v)
    {
        NewBase = FindCommonAncestor(u,v);
        memset(InBlossom,false,sizeof(InBlossom));
        ResetTrace(u);
        ResetTrace(v);
        if(Base[u] != NewBase) Father[u] = v;
        if(Base[v] != NewBase) Father[v] = u;
        for(int tu = 1; tu <= N; tu++)
            if(InBlossom[Base[tu]])
            {
                Base[tu] = NewBase;
                if(!InQueue[tu]) Push(tu);
            }
    }
    void FindAugmentingPath()
    {
        memset(InQueue,false,sizeof(InQueue));
        memset(Father,0,sizeof(Father));
        for(int i = 1;i <= N;i++)
            Base[i] = i;
        Head = Tail = 1;
        Push(Start);
        Finish = 0;
        while(Head < Tail)
        {
            int u = Pop();
            for(int v = 1; v <= N; v++)
                if(Graph[u][v] && (Base[u] != Base[v]) && (Match[u] != v))
                {
                    if((v == Start) || ((Match[v] > 0) && Father[Match[v]] > 0))
                        BloosomContract(u,v);
                    else if(Father[v] == 0)
                    {
                        Father[v] = u;
                        if(Match[v] > 0)
                            Push(Match[v]);
                        else
                        {
                            Finish = v;
                            return;
                        }
                    }
                }
        }
    }
    void AugmentPath()
    {
        int u,v,w;
        u = Finish;
        while(u > 0)
        {
            v = Father[u];
            w = Match[v];
            Match[v] = u;
            Match[u] = v;
            u = w;
        }
    }
    void Edmonds()
    {
        memset(Match,0,sizeof(Match));
        for(int u = 1; u <= N; u++)
            if(Match[u] == 0)
            {
                Start = u;
                FindAugmentingPath();
                if(Finish > 0)AugmentPath();
            }
    }
    int getMatch()
    {
        Edmonds();
        Count = 0;
        for(int u = 1; u <= N;u++)
            if(Match[u] > 0)
                Count++;
        return Count/2;
    }
    
    bool g[MAXN][MAXN];
    pair<int,int>p[150];
    int main()
    {
        //freopen("in.txt","r",stdin);
        //freopen("out.txt","w",stdout);
        int m;
        while(scanf("%d%d",&N,&m)==2)
        {
            memset(g,false,sizeof(g));
            memset(Graph,false,sizeof(Graph));
            int u,v;
            for(int i = 1;i <= m;i++)
            {
                scanf("%d%d",&u,&v);
                p[i] = make_pair(u,v);
                g[u][v] = true;
                g[v][u] = true;
                Graph[u][v] = true;
                Graph[v][u] = true;
            }
            int cnt0 = getMatch();
            //cout<<cnt0<<endl;
            vector<int>ans;
            for(int i = 1;i <= m;i++)
            {
                u = p[i].first;
                v = p[i].second;
                memcpy(Graph,g,sizeof(g));
                for(int j = 1;j <= N;j++)
                    Graph[j][u] = Graph[u][j] = Graph[j][v] = Graph[v][j] = false;
                int cnt = getMatch();
                //cout<<cnt<<endl;
                if(cnt < cnt0-1)
                    ans.push_back(i);
            }
            int sz = ans.size();
            printf("%d
    ",sz);
            for(int i = 0;i < sz;i++)
            {
                printf("%d",ans[i]);
                if(i < sz-1)printf(" ");
            }
            printf("
    ");
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Linux 文件系统层次结构 笔记 day 02
    新买服务器 设置ssh秘钥登录
    FastSocket客户端/服务端通讯示例 客户端被动接收
    FastSocket学习笔记~制定自已的传输协议~续~制定基于FastSocket的协议
    Oracle 查看表空间使用率
    Oracle查看用户占用的表空间大小
    failed to flush export bulk [default_local]
    Elasticsearch7.x配置文件
    master not discovered yet, this node has not previously joined a bootstrapped (v7+) cluster
    4.4 rust Smart Pointers Box and Deref 及 空针指
  • 原文地址:https://www.cnblogs.com/gongpixin/p/5360660.html
Copyright © 2011-2022 走看看