ZOJ_3933_Team Formation(二分图最佳匹配)

版权声明: https://blog.csdn.net/yyy_3y/article/details/80009836

传送门

题意:有n个人,第二行告诉你他是0队还是1队。第三行告诉你是男生还是女生。
现在想要组队,每个队伍必须是一个0队和一个1队的人。
问在组队数尽可能多的情况下,女生的参数人数要最大。
思路:学习KM算法的第一题。带权匹配可以用km算法也可以用费用流解。在km算法中这里的权值我们可以尽可能的设的大一些。例如我设置为10000.一个队伍中每有一个女生权值就++。这样能保证最后在边最多的情况下,女生最多。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int N=550;
const int inf=0x3f3f3f3f;
int n,nx,ny;
int match[N],lx[N],ly[N],slack[N];
int visx[N],visy[N],w[N][N];
int dfs(int x)
{
    visx[x]=1;
    for(int y=1;y<=ny;y++){
        if(visy[y]) continue;
        int tmp=lx[x]+ly[y]-w[x][y];
        if(!tmp){
            visy[y]=1;
            if(match[y]==-1 || dfs(match[y])){
                match[y]=x;
                return 1;
            }
        }
        else if(slack[y]>tmp) slack[y]=tmp;
    }
    return 0;
}
int KM()
{
    int i,j;
    memset(match,-1,sizeof(match));
    memset(ly,0,sizeof(ly));
    for(i=1;i<=nx;i++)
        for(j=1,lx[i]=-inf;j<=ny;j++)
            if(w[i][j]>lx[i]) lx[i]=w[i][j];
    for(int x=1;x<=nx;x++){
        for(i=1;i<=ny;i++)
            slack[i]=inf;
        while(1){
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(dfs(x)) break;
            int d=inf;
            for(int i=1;i<=ny;i++)
                if(!visy[i] && d>slack[i]) d=slack[i];
            for(int i=1;i<=nx;i++)
                if(visx[i]) lx[i]-=d;
            for(int i=1;i<=ny;i++)
                if(visy[i]) ly[i]+=d;
                else slack[i]-=d;
        }

    }
    int ans=0;
    for(int i=1;i<=ny;i++)
        if(match[i]!=-1)
            ans+=w[match[i]][i];
    return ans;
}

int vis[N][N];
char group[N],sex[N];
int main(){

    //yyy_3y
    int T; scanf("%d",&T);
    while(T--){
        int n;scanf("%d",&n);
        memset(vis,0,sizeof(vis));
        memset(w,0,sizeof w);
        scanf("%s %s",group+1,sex+1);
        nx=ny=n;
        for(int i=1;i<=n;i++){
            int m; scanf("%d",&m);
            for(int j=1;j<=m;j++){
                int tmp; scanf("%d",&tmp);
                vis[i][tmp]=vis[tmp][i]=1;
            }
            for(int j=1;j<=n;j++){
                if(!vis[i][j] && group[i]!=group[j]){
                    int ret=10000;
                    if(sex[i]=='0') ret++;
                    if(sex[j]=='0') ret++;
                    vis[i][j]=vis[j][i]=1;
                    if(group[i]=='1') w[i][j]=ret;
                    else w[j][i]=ret;
                }
            }
        }
        int ans=KM();
        printf("%d %d\n",ans/10000,ans%10000);
        for(int i=1;i<=n;i++){
            if(match[i]!=-1 && w[match[i]][i]) printf("%d %d\n",match[i],i);
        }
    }




    return 0;
}
/*
2
3
101
000
0
0
0
3
101
000
0
0
0
*/

猜你喜欢

转载自blog.csdn.net/yyy_3y/article/details/80009836