现在的位置: 首页 > 综合 > 正文

二分图多重匹配

2013年05月29日 ⁄ 综合 ⁄ 共 3577字 ⁄ 字号 评论关闭

pku2899 Jamie's Contact Groups

题意:在通讯录中有N个人,每个人能可能属于多个group,现要将这些人分组m组,设各组中的最大人数为max,求出该最小的最大值

分析:二分group的最大值,+二分图多重匹配

View Code

#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
const int N = 1000+10;
const int M = 500+10;
int n,m;
int map[N][M],vlink[M],link[M][N],max_cap;
bool vis[M];
int path(int s)
{
    for(int i=0;i<m;i++)
    {
        if(map[s][i] && !vis[i])
        {
            vis[i]=true;
            if(vlink[i]<max_cap)
            {
                link[i][vlink[i]++]=s;
                return 1;
            }
            for(int j=0;j<vlink[i];j++)
            {
                if(path(link[i][j]))
                {
                    link[i][j]=s;
                    return 1;
                }
            }
        }
    }
    return 0;
}
bool max_match(int mid)
{
    max_cap=mid;
    memset(vlink,0,sizeof(vlink));
    for(int i=0;i<n;i++)
    {
        memset(vis,false,sizeof(vis));
        if(!path(i))
            return false;
    }
    return true;
}
int main()
{
    char str[20],c;
    int a;
    while(scanf("%d %d",&n,&m)==2 && (n||m))
    {
        memset(map,false,sizeof(map));
        for(int i=0;i<n;i++)
        {
            scanf("%s",str);
            while(true)
            {
                scanf("%d%c",&a,&c);
                map[i][a]=true;
                if(c=='\n') break;
            }
        }
        int left=1,right=n,ans=n;
        while(left<=right)
        {
            int mid=(left+right)>>1;
            if(max_match(mid))
            {
                if(mid<ans)
                    ans=mid;
                right=mid-1;
            }
            else left=mid+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}
                

 pku3189 Steady Cow Assignment

题意:在所有的cow眼中,被分到不同的barns有不同的满意度,将N只cow分到B个barns中,使得最大的barns满意度差值最小

分析:还是这种最大最小问题,二分枚举满意度的差值,根据差值枚举不同的满意度区间,判断是否可行

View Code

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
using namespace std;
const int N = 1000+10;
const int M = 20+10;
int n,m,cap[M],link[M][N],vlink[M],max_range;
vector<int> g[N];
bool vis[N];
int path(int r,int s)
{
    int size=g[s].size();
    for(int i=0;i<size;i++)//vector是按满意度将barns对应的编号加入,所以i+1就是s对g[s][i]的满意度
    {
        int v=g[s][i];
        if(!vis[v] && i+1>=r && i+1<=max_range+r-1)//满足该区间才可匹配
        {
            vis[v]=true;
            if(vlink[v]<cap[v])
            {
                link[v][vlink[v]++]=s;
                return 1;
            }
            for(int j=0;j<vlink[v];j++)
            {
                if(path(r,link[v][j]))
                {
                    link[v][j]=s;
                    return 1;
                }
            }
        }
    }
    return 0;
}
bool max_match(int mid)
{
    max_range=mid;

    for(int j=1;j<=m-max_range+1;j++)//枚举满意度的区间的起点
    {    
        memset(vlink,0,sizeof(vlink));
        bool flag=true;
        for(int i=0;i<n;i++)
        {
            memset(vis,false,sizeof(vis));
            if(!path(j,i))
            {
                flag=false;
                break;
            }
        }
        if(flag) //满足则返回,继续二分
            return true;
    }
    return false;
}
int main()
{
    int a;
    while(scanf("%d %d",&n,&m)==2)
    {
        for(int i=0;i<n;i++)
        {
            g[i].clear();
            for(int j=0;j<m;j++)
            {
                scanf("%d",&a);
                g[i].push_back(a);
            }
        }
        for(int i=1;i<=m;i++)
            scanf("%d",&cap[i]);
        int left=1,right=m,ans=m;
        while(left<=right)//二分枚举满意度的差值
        {
            int mid=(left+right)>>1;
            if(max_match(mid))
            {
                ans=min(mid,ans);
                right=mid-1;
            }
            else left=mid+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

 pku1698 Alice's Chance

题意:有M部电影要拍,规定每一部电影只能在每周的某几天拍,给定每一部电影的总拍摄日期以及截至周数,问能否拍完所有的电影,(每天只能拍一部电影)

分析:还是一样的多重匹配,x集是日期,y集是电影,将日期分配给有关联的电影;

最后判断是否能拍完只需看一下每一部电影是否被分配了足够的日期

当然,肯定是可以用网络流做了。

将电影作为X方点,每一天作为Y方点(最多50周,每周7天,所以共设350个Y方点),若第i个电影可以在第j天拍就连边(i, j)。每个X方点的L值为该电影总共要搞多少天,

每个Y方点的L值为1(每天最多只能搞一个电影),若能使所有从源点连向X方点的边都满流,则输出Yes,否则输出No。

多重匹配

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
using namespace std;
const int M = 20+10;
const int N = 350+10;
int link[M][N],vlink[M],cap[M];
bool vis[M];
vector<int> g[N];
void init()
{
    for(int i=0;i<N;i++)
        g[i].clear();
}
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(vlink[v]<cap[v])
            {
                link[v][vlink[v]++]=s;
                return 1;
            }
            for(int j=0;j<vlink[v];j++)
            {
                if(path(link[v][j]))
                {
                    link[v][j]=s;
                    return 1;
                }
            }
        }
    }
    return 0;
}
bool max_match(int n,int m)
{
    memset(vlink,0,sizeof(vlink));
    for(int i=0;i<n;i++)
    {
        if(g[i].size()==0)
            continue;
        memset(vis,false,sizeof(vis));
        path(i);
    }
    for(int i=0;i<m;i++)
    {
        if(vlink[i]!=cap[i])
            return false;
    }
    return true;
}
int main()
{
    int T;
    int n,m,a[10],w,maxw;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&m);
        maxw=0;
        init();
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<7;j++)
                scanf("%d",&a[j]);
            scanf("%d %d",&cap[i],&w);
            maxw=max(maxw,w);
            for(int j=0;j<7;j++)
            {
                if(a[j])
                {
                    for(int k=0;k<w;k++)
                        g[k*7+j].push_back(i);
                }
            }
        }
        n=maxw*7;
        if(max_match(n,m))
            puts("Yes");
        else puts("No");
    }
    return 0;
}

 

抱歉!评论已关闭.