2017暑假集训 div1 匹配问题(1)

A ZOJ 1002
题意:放棋子,棋子横竖不能相见。
做法:直接暴力DFS就行了。n很小只有4


B HDU2444
题意:给定n个学生,他们之间可能互相认识,首先判断能不能将这些学生分为两组,使组内学生不认识;

现想将学生两两分组,且保证每一组的学生都认识,这样分组可达到的最大组数为多大?

做法:首先用BFS染色判断是否是二分图,相连的边异色,如果不是则直接NO就行了。然后跑一边匈牙利算法,最后问的是几对可以匹配成功,那答案输出的时候要除以2


#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <string.h>
#include <queue>
using namespace std;
const int maxn=250;
int n,m;
vector<int> g[maxn];
int col[maxn];
bool judge()
{
    memset(col,-1,sizeof(col));
    col[1]=1;
    queue<int> que;
    que.push(1);
    while(!que.empty())
    {
        int u=que.front(); que.pop();
        int next=1-col[u];
        for(int i=0;i<g[u].size();i++)
        {
            int v=g[u][i];
            if(col[v]==-1)
            {
                col[v]=next;
                que.push(v);
            }
            else
            {
                if(col[v]==col[u]) return 1;
            }
        }
    }
    return 0;
}

bool used[maxn];
int boy[maxn];
bool f(int u)
{
    for(int i=0;i<g[u].size();++i)
    {
        int v=g[u][i];
        if(used[v]) continue;
        used[v]=1;
        if(boy[v]==-1||f(boy[v]))
        {
            boy[v]=u;
            return 1;
        }
    }
    return 0;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;++i) g[i].clear();
        for(int i=1;i<=m;++i)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            g[a].push_back(b);  g[b].push_back(a);
        }
        if(judge())  printf("No\n");
        else
        {
            int ans=0;
            memset(boy,-1,sizeof(boy));
            for(int i=1;i<=n;++i)
            {
                memset(used,0,sizeof(used));
                if(f(i)) ans++;
            }
            printf("%d\n",ans/2);
        }
    }
    return 0;
}

HDU 1083
题意:有p门课程,n个学生,每个学生在一个 committee代表一门课程,求学生和课程的最大匹配,若最大匹配等于p,输出YES,否则输出NO。

做法:直接二分匹配上模板


HDU 1281
题意:中文题意
做法:建图是关键,把x看做一个集合,y看做一个集合(x,y)代表x可以连y。然后跑一边最大匹配,然后枚举所有边,如果跑出来的答案与第一次不一样就是关键点,
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
#include <vector>
using namespace std;
int n,m,k;
bool map[105][105];
struct node
{
    int x,y;
}p[100*105];

int boy[150];
int used[150];
bool f(int u)
{
    for(int i=1;i<=m;++i)
    {
        if(map[u][i]&&!used[i])
        {
            used[i]=1;
            if(boy[i]==-1 || f(boy[i]))
            {
                boy[i]=u;
                return 1;
            }
        }
    }
    return 0;
}
int pp()
{
    memset(boy,-1,sizeof(boy));
    int l=0;
    for(int i=1;i<=n;++i)
    {
        memset(used,0,sizeof(used));
        if(f(i)) l++;
    }
    return l;
}
int main()
{
    int kiss=0;
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        memset(map,0,sizeof(map));
        for(int i=1;i<=k;++i)
        {
            scanf("%d%d",&p[i].x,&p[i].y);
            map[p[i].x][p[i].y]=1;
        }
        int l=pp();
        int flag=0;
        for(int i=1;i<=k;++i)
        {
            map[p[i].x][p[i].y]=0;
            int temp=pp();
            if(temp!=l) flag++;
            map[p[i].x][p[i].y]=1;
        }
        printf("Board %d have %d important blanks for %d chessmen.\n",++kiss,flag,l);
    }
    return 0;
}

HDU 2819

题意:交换图的某些行或者是某些列(可以都换),使得这个N*N的图对角线上全部都是1.
注意: 这里有一点需要说明,就是说题目的交换,其实是将原来图的某一行移到最后图的某一行,而不是指先交换两行,得到一个新图,再交换新图的两行。感觉这里比较坑。
    这里先说明的一点就是,如果通过交换某些行没有办法的到解的话,那么只交换列 或者 既交换行又交换列 那也没办法得到解。这个可以用矩阵的秩来解释,所有的对角线都是1,所以也就是矩阵的秩就是N,所以秩小于N就无解。另外,根据矩阵的性质,任意交换矩阵的两行  或者  两列,矩阵的秩不变,也就保证了如果通过 只交换行  或  只交换列 无法得到解的话,那么其他交换形式也必然无解。
做法:   既然说是用二分图的最大匹配,那怎么构建二分图呢,我们构建的二分图,第一部分X表示的是横坐标,第二部分Y表示纵坐标,所以范围都是1~N,然后如果a[i][j]是1,那我们就从X的i向Y的j引一条边,那么这条边的含义就可以解释为可以将Y的第j列(因为Y表示的是列的集合)移到第i列,使得a[i][i]变成1,这样就相当于是第i行第i列就变成了1,也就是说对角线多了一个1。
    因此我们求这个二分图的最大匹配(目的是为了让每一列只与X中的某一行匹配),这样来就形成了N条边,那我们只需要将所有匹配的边的右边(列)  和  左边(行)所在的列  交换,这样一来对角线上这一行就成了1.
    上面也也正好提示了如果最大匹配是N,那就存在解,否则无解。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;

bool map[110][110];
int boy[110];
int used[110];
int n;
int x[2000],y[2000];
bool f(int u)
{
    for(int i=1;i<=n;++i)
    {
       if(map[u][i]&&!used[i])
       {
           used[i]=1;
           if(boy[i]==-1 || f(boy[i]) )
           {
                boy[i]=u; return 1;
           }
       }
    }
    return 0;
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        memset(map,0,sizeof(map));
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=n;++j)
            {
                int c;
                scanf("%d",&c);
                if(c) map[i][j]=1;
            }
        }
        memset(boy,-1,sizeof(boy));
        int ans=0;
        for(int i=1;i<=n;++i)
        {
            memset(used,0,sizeof(used));
            if(f(i)) ans++;
        }
        if(ans<n)
        {
            printf("-1\n");
            continue;
        }
        else
        {
            int ans=0;
            for(int i=1;i<=n;++i)
            {
                for(int j=1;j<=n;++j)
                {
                    if(boy[j]==i&&i!=j)
                    {
                      x[ans]=i; y[ans++]=j;
                      swap(boy[i],boy[j]);
                      break;
                    }
                }
            }
            printf("%d\n",ans);
            for(int i=0;i<ans;++i)
            {
                printf("C %d %d\n",x[i],y[i]);
            }
        }
    }
    return 0;
}






















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值