图论:二分图多重匹配

使用最大流和费用流解决二分图的多重匹配

之前编辑的忘存了好气啊。。

本来打算学完二分图的乱七八糟的匹配之后再去接触网络流的,提前撞到了

之前我们说的二分图最大匹配和二分图最大权匹配有一个特点,那就是没个点只能与一条边相匹配

如果规定一个点要与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 }

据说这是典型的最大流题目,然而为了强行安利一波二分图的多重匹配,就不说成那个了

转载于:https://www.cnblogs.com/aininot260/p/9441001.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值