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

来源:互联网 发布:淘宝福袋是死人衣服 编辑:程序博客网 时间:2024/05/29 07:46
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;}






















原创粉丝点击