• 【二分图】文理分班


    【二分图】文理分班

    标签(空格分隔): 图论 二分图


    【前提级】

    此文理分班非彼文理分班,如果找的是bzoj的那道文理分班可以绕道了

    【题目】

    题目描述

    jzyz每年的文理分班的时候,每个班都会有一些同学分到其他班,还会进入一些其他班的同学进入这个班。

    小x负责安排座位,为了照顾分班带来的那种伤感情绪,小x制定了很人性化的座位安排计划,具体计划如下:

    比如A和B都是本班学生且是好朋友,A分到了其他班,而C则是外班进入这个班的,C和A并不熟悉,而C和B关系很好,那么小x为了照顾A和C的情绪,就会让B坐在A的位置,C坐在B的位置。

    当然,实际情况可能很复杂,比如一个班里的同学之间关系不一定好,外班进来的可能和本班很多人关系都很好。

    现在告诉你,和小x所在班有关系的人一共有n个人,小x想知道有没有一个合理的方案来满足自己的座位安排计划。

    输入格式

    本题为多组数据,第一行一个整数M,表示有M组测试数据。

    对于每组测试数据,每组的第一行一个整数n,表示一共有n个人和这个班有关系。

    接下来一行n个整数,第i个整数表示第i个人是否是本班学生(0表示不是,1表示是,分到其他班的也算是本班学生)

    接下来一行n个整数,第i个整数表示第i个人是否要分到其他班(0表示留在本班,1表示分到其他班,如果第i个人是由外班分进来的,那么第i个整数就是一个随机整数,没有实际意义)

    接下来是一个n行n列的一个二维矩阵,第i行第j列的数表示第i个人和第j个人是否关系很好(1表示认识,0表示不认识),对角线上是0,但是自己肯定认识自己。

    输出格式

    每组数据,如果存在一个方案,输出 “^_^”(不含引号)。

    如果没有方案,输出 “T_T”(不含引号)。都是半角字符。

    输入输出样例

    输入 #1
    1
    3
    1 1 0
    0 1 0
    0 1 1
    1 0 0
    1 0 0
    输出 #1
    ^_^

    【思路】

    这题是二分图中第一道需要自己判断谁左谁右的题,拿到时很懵,搜了下题解发现只有一篇,然而只有代码,没有思路,就很气,结果自己发现有个地方还是不理解,如果有错误请指出,希望来个兄弟能有个说法。
    左右子图表示可以换座,我们需要建立一个这样的图,跑一遍匈牙利,但是建图的过程比较难以理解。
    直接看代码中的注释吧,有点难表述。

    【代码】

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    using namespace std;
    const int maxn=1e3+500,INF=0x3f3f3f3f;
    int n,m,t,vis[maxn],girls[maxn],a[maxn],b[maxn],p[maxn][maxn];
    vector<int> g[maxn];
    inline int read(){
    	int s=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){
    		if(ch=='-')w=-1;ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    	return s*w;
    }
    bool Find(int u){
    	for(int i=0;i<g[u].size();i++){
    		int v=g[u][i];
    		if(!vis[v]){
    			vis[v]=1;
    			if(girls[v]==0||Find(girls[v])){
    				girls[v]=u;
    				return 1;
    			}
    		}
    	}
    	return 0;
    	
    }//板子不会建议回炉
    int main(){
    	freopen("a.in","r",stdin);
    	t=read();
    	while(t--){
    		for(int i=0;i<maxn;i++)g[i].clear();
    		n=read();
    		for(int i=1;i<=n;i++)a[i]=read();
    		for(int i=1;i<=n;i++){
    			b[i]=read();
    			//if(b[i]!=0&&b[i]!=1)b[i]=0;//肯定是隔壁班的人需要分进来的,这里写是为了方便理解,不写也行,因为我后来观摩题面,发现其实所有隔壁班的都一定需要分进来,题目中有说明,那分不进来肯定就会输出no
    			if(a[i]==1&&b[i]==0)g[i].push_back(i);//如果自己在本班且自己不被分出去,肯定可以和自己换座(可能有些人爆零在这里)
    		}
    		for(int i=1;i<=n;i++){
    			for(int j=1;j<=n;j++){
    				int x=read();
    			//	if(a[j]==0)x=0;
    				if(x==1&&a[j]==1){//i和j是好朋友,且j是本班的,i就可以换j
    					g[i].push_back(j);//i能换j不代表j换i,因为i可能不在本班又不分进本班,此时j就不可能换i
    				}
    			}
    		}
    		bool flag=0;
    		memset(girls,0,sizeof(girls));
    		for(int i=1;i<=n;i++){
    			memset(vis,0,sizeof(vis));
    			if(!a[i]||(a[i]&&!b[i])){//先解释判断后面,如果i在本班,且不被分出去,肯定会被换座。判断前面i不在本班的情况我一开始不是很懂,但是题目中说所有人都与本班有关系,所以i不在本班肯定会需要分进本班,所以需要查找
    				if(!Find(i)){
    					cout<<"T_T"<<endl;
    					flag=1;
    					break;
    				}
    			}
    		}
    		if(flag==0)cout<<"^_^"<<endl;
    	}
    	
    }
    

    题外话

    可能有的地方理解不对,望指出

  • 相关阅读:
    css之隐藏内容的方法
    字符串对象的各种方法
    javascript之自增自减典型运算(易错)
    Fuel 30 分钟快速安装OpenStack
    使用Sublime Text 3做Python开发
    Ubuntu 14.04.02 安装openvswitch-2.3.1
    linux 技巧:使用 screen 管理你的远程会话
    MQTT的学习研究(十七)Mosquitto简要教程(安装&使用)
    MQTT的学习研究(十六) MQTT的Mosquitto的window安装部署
    MQTT的学习研究(十五) MQTT 和android整合文章
  • 原文地址:https://www.cnblogs.com/614685877--aakennes/p/12841088.html
Copyright © 2020-2023  润新知