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
  • 相关阅读:
    Software Solutions CACHE COHERENCE AND THE MESI PROTOCOL
    CACHE COHERENCE AND THE MESI PROTOCOL
    Multiprocessor Operating System Design Considerations SYMMETRIC MULTIPROCESSORS
    Organization SYMMETRIC MULTIPROCESSORS
    PARALLEL PROCESSING
    1分钟内发送差评邮件
    Secure Digital
    SYMMETRIC MULTIPROCESSORS
    A Taxonomy of Parallel Processor Architectures
    parallelism
  • 原文地址:https://www.cnblogs.com/gongpixin/p/5360660.html
Copyright © 2011-2022 走看看