圖論:二分圖多重匹配


使用最大流和費用流解決二分圖的多重匹配

之前編輯的忘存了好氣啊。。

本來打算學完二分圖的亂七八糟的匹配之后再去接觸網絡流的,提前撞到了

之前我們說的二分圖最大匹配和二分圖最大權匹配有一個特點,那就是沒個點只能與一條邊相匹配

如果規定一個點要與L條邊相匹配,這樣的問題就是二分圖的多重匹配問題

然后根據邊是否帶權重,又可以分為二分圖最大多重匹配和二分圖最大權多重匹配(二分圖多重最佳完美匹配)

首先給出二分圖多重最大匹配的做法:

在原圖上建立源點S和匯點T,S向每個X方點連一條容量為該X方點L值的邊,每個Y方點向T連一條容量為該Y方點L值的邊

原來二分圖中各邊在新的網絡中仍存在,容量為1(若該邊可以使用多次則容量大於1),求該網絡的最大流,就是該二分圖多重最大匹配的值

然后給出二分圖多重最優匹配(二分圖多重最大權匹配)的做法:

在原圖上建立源點S和匯點T,S向每個X方點連一條容量為該X方點L值、費用為0的邊,每個Y方點向T連一條容量為該Y方點L值、費用為0的邊

原來二分圖中各邊在新的網絡中仍存在,容量為1(若該邊可以使用多次則容量大於1),費用為該邊的權值。求該網絡的最大費用最大流,就是該二分圖多重最優匹配的值

這道題里面,一共有X方點這么多的電影,每個電影需要拍攝多少天就是對應的X方點L值,然后每一天是一個Y方點,由於每一天只能拍攝一部電影,所有Y方點的L值均為1

下面介紹一下實現:

int n,sum,cnt,ans;
int g[maxn],cur[maxn];
int str[25][10];
struct Edge{int u,v,next,cap,flow;}e[maxm];

這里面的cur數組是g數組的臨時數組

str用來保存每一個電影可以在哪一天拍攝

Edge是網絡流圖里面的邊

void addedge(int u,int v,int c)
{
    e[++cnt].u=u;e[cnt].v=v;e[cnt].cap=c;
    e[cnt].flow=0;e[cnt].next=g[u];g[u]=cnt;
    
    e[++cnt].u=v;e[cnt].v=u;e[cnt].cap=0;
    e[cnt].flow=0;e[cnt].next=g[v];g[v]=cnt;
}

建圖的時候,注意怎么賦值的

接下來根據題意建圖:

for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=7;j++)
                scanf("%d",&str[i][j]);    
            scanf("%d%d",&d,&w);
            sum+=d;
            addedge(0,i,d);  //容量為需要多少天 
            for(int j=1;j<=7;j++)
                for(int k=0;k<w;k++)
                    if(str[i][j]) addedge(i,20+k*7+j,1);
        }
        for(int i=21;i<=370;i++) addedge(i,371,1);
        ans=maxflow(0,371);

0為源點,371為匯點

sum最后進行一個統計,和源點出發的最大流量進行比較,如果相等,說明電影排的開

然后是求最大流的一個板子

int maxflow(int st,int ed)
{
    int flowsum=0;
    while(bfs(st,ed))
    {
        memcpy(cur,g,sizeof(g));
        flowsum+=dfs(st,ed,INF);
        //cout<<"#"<<flowsum<<" ";
    }
    return flowsum;
    
}

具體的DFS和BFS這里不作為重點,以后再說

下面給出完整的實現:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 const int INF=1000000000;
  6 const int maxn=1005;
  7 const int maxm=20005;
  8 int n,sum,cnt,ans;
  9 int g[maxn],cur[maxn];
 10 int str[25][10];
 11 struct Edge{int u,v,next,cap,flow;}e[maxm];
 12 void addedge(int u,int v,int c)
 13 {
 14     e[++cnt].u=u;e[cnt].v=v;e[cnt].cap=c;
 15     e[cnt].flow=0;e[cnt].next=g[u];g[u]=cnt;
 16     
 17     e[++cnt].u=v;e[cnt].v=u;e[cnt].cap=0;
 18     e[cnt].flow=0;e[cnt].next=g[v];g[v]=cnt;
 19 }
 20 int q[maxn],vis[maxn],d[maxn];
 21 bool bfs(int st,int ed)
 22 {
 23     memset(q,0,sizeof(q));
 24     memset(vis,0,sizeof(vis));
 25     memset(d,-1,sizeof(d));
 26     vis[st]=1;d[st]=0;
 27     int h=0,t=1;
 28     q[t]=st;
 29     while(h!=t)
 30     {
 31         h=h%maxn+1;
 32         int u=q[h];
 33         for(int tmp=g[u];tmp;tmp=e[tmp].next)
 34         {
 35             if(!vis[e[tmp].v]&&e[tmp].cap>e[tmp].flow)
 36             {
 37                 vis[e[tmp].v]=1;
 38                 d[e[tmp].v]=d[u]+1;
 39                 if(e[tmp].v==ed) return true;
 40                 t=t%maxn+1;
 41                 q[t]=e[tmp].v;
 42             }
 43         }
 44     }
 45     return false;
 46 }
 47 int getpair(int x)
 48 {
 49     if(x%2==0)
 50         return x-1;
 51     else return x+1;
 52 }
 53 int dfs(int x,int ed,int a)
 54 {
 55     if(x==ed||a==0) return a;
 56     int flow=0,f;
 57     for(int tmp=cur[x];tmp;tmp=e[tmp].next)
 58     {
 59         if(d[e[tmp].v]==d[x]+1&&(f=dfs(e[tmp].v,ed,min(a,e[tmp].cap-e[tmp].flow)))>0)
 60         {
 61             e[tmp].flow+=f;
 62             e[getpair(tmp)].flow-=f;
 63             a-=f;
 64             flow+=f;
 65             if(a==0) break;
 66         }
 67     }
 68     return flow;
 69 }
 70 int maxflow(int st,int ed)
 71 {
 72     int flowsum=0;
 73     while(bfs(st,ed))
 74     {
 75         memcpy(cur,g,sizeof(g));
 76         flowsum+=dfs(st,ed,INF);
 77         //cout<<"#"<<flowsum<<" ";
 78     }
 79     return flowsum;
 80     
 81 }
 82 void init()
 83 {
 84     sum=cnt=0;
 85     memset(g,0,sizeof(g));
 86 }
 87 int main()
 88 {
 89     int T,d,w;
 90     scanf("%d",&T);
 91     while(T--)
 92     {
 93         init();
 94         scanf("%d",&n);
 95         for(int i=1;i<=n;i++)
 96         {
 97             for(int j=1;j<=7;j++)
 98                 scanf("%d",&str[i][j]);    
 99             scanf("%d%d",&d,&w);
100             sum+=d;
101             addedge(0,i,d);  //容量為需要多少天 
102             for(int j=1;j<=7;j++)
103                 for(int k=0;k<w;k++)
104                     if(str[i][j]) addedge(i,20+k*7+j,1);
105         }
106         for(int i=21;i<=370;i++) addedge(i,371,1);
107         ans=maxflow(0,371);
108         if(ans==sum) printf("Yes\n");
109         else printf("No\n");
110     }
111     return 0;
112 }

據說這是典型的最大流題目,然而為了強行安利一波二分圖的多重匹配,就不說成那個了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM