BZOJ1433 ZJOI2009假期的宿舍 计蒜课习题-座谈会的椅子分配 二分图

用dinic解决最大匹配  拆点分别表示椅子和人

 

1433: [ZJOI2009]假期的宿舍

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 3612  Solved: 1540
[Submit][Status][Discuss]

Description

学校放假了······有些同学回家了,而有些同学则有以前的好朋友来探访,那么住宿就是一个问题。比如A
和B都是学校的学生,A要回家,而C来看B,C与A不认识。我们假设每个人只能睡和自己直接认识的人的床。那么一
个解决方案就是B睡A的床而C睡B的床。而实际情况可能非常复杂,有的人可能认识好多在校学生,在校学生之间也
不一定都互相认识。我们已知一共有n个人,并且知道其中每个人是不是本校学生,也知道每个本校学生是否回家
。问是否存在一个方案使得所有不回家的本校学生和来看他们的其他人都有地方住。
 

Input

第一行一个数T表示数据组数。接下来T组数据,
每组数据第一行一个数n表示涉及到的总人数。
接下来一行n个数,第i个数表示第i个人是否是在校学生(0表示不是,1表示是)。
再接下来一行n个数,第i个数表示第i个人是否回家
(0表示不回家,1表示回家,注意如果第i个人不是在校学生,那么这个位置上的数是一个随机的数,
你应该在读入以后忽略它)。
接下来n行每行n个数,
第i行第j个数表示i和j是否认识
(1表示认识,0表示不认识,第i行i个的值为0,但是显然自己还是可以睡自己的床),
认识的关系是相互的。
1 ≤ n ≤ 50,1 ≤ T ≤ 20
 

Output

对于每组数据,如果存在一个方案则输出“^_^”(不含引号)否则输出“T_T”(不含引号)。
(注意输出的都是半角字符,即三个符号的ASCII码分别为94,84,95)

Sample Input

1
3
1 1 0
0 1 0
0 1 1
1 0 0
1 0 0

Sample Output

ˆ ˆ

HINT

 

Source

代码注释里的员工就是在校生(计蒜客题面是员工

匈牙利算法版本

#include<bits/stdc++.h>
using namespace std;
const int N=1007;
int n;
struct edge{int v,next;}e[1000007];
int p[N],eid=0;
int link[N];
bool vis[N],ism[N],isj[N];
bool dfs(int u){
    int v;
    for(int i=p[u];~i;i=e[i].next){
        v=e[i].v;
        if(!vis[v]){
            vis[v]=true;
            if(link[v]==-1||dfs(link[v])){
                link[v]=u;
                return true;
            }
        }
    }
    return false;
}
int hungary(){
    int res=0;
    memset(link,-1,sizeof(link));
    for(int i=1;i<=n;i++){
        if((ism[i]&&!isj[i])||!ism[i])
        {	
			memset(vis,0,sizeof(vis));
   			res+=dfs(i);
		} 
    }
    return res;
}
inline void init(){
    memset(p,-1,sizeof(p));
    memset(ism,false,sizeof(ism));
    memset(isj,true,sizeof(isj));
    eid=0;
}
inline void ins(int u,int v){ e[eid].v=v; e[eid].next=p[u]; p[u]=eid++; }
inline int read(){
    int s=0,f=1;char c=getchar();
    while(c>'9'||c<'0') {if(c=='-') f=-1; c=getchar(); }
    while(c>='0'&&c<='9') {s= s*10+c-48;c=getchar();}
    return s*f;
}
int T;
int main(){
    
    T=read();
    while(T--){
        init();
        int tot=0;
        n=read();
        for(int i=1;i<=n;i++){
            ism[i]=read();
            if(!ism[i]){    //不是公司员工
                tot++;
            }
        }
        for(int i=1;i<=n;i++){
            isj[i]=read()>0?1:0;
            if((!isj[i])&&ism[i]) {ins(i,i);tot++;}
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(read()==1&&ism[j]) //认识的有座位的人
                    ins(i,j);
            }
        }
        //printf("%d %d\n",hungary(),tot);
        printf(hungary()==tot?"^_^\n":"T_T\n");
    }
    return 0;
}

dinic版本

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N = 2007, INF = 0x3f3f3f3f;
//点的编号由0~2001
int t, S, T, n;
int d[N];
bool ism[N];	//员工
struct edge { int  v, next, c; }e[4000002];
int p[N], eid = 0, tot = 0;
bool bfs() {
	memset(d, -1, sizeof(d));
	queue<int> q;
	q.push(S);
	d[S] = 0;
	while (!q.empty()) {
		int u = q.front(); q.pop();
		int v;
		for (int i = p[u]; ~i; i = e[i].next) {
			v = e[i].v;
			if (e[i].c > 0 && d[v] == -1) {
				d[v] = d[u] + 1;
				q.push(v);
				if (v == T) return true;
			}
		}
	}
	return (d[T] != -1);		//构建层次网络 并返回能否到达T点
}
int dfs(int u, int flow) {
	if (u == T) return flow;
	int res = 0, v;
	for (int i = p[u]; ~i; i = e[i].next) {
		v = e[i].v;
		if (e[i].c > 0 && d[u] + 1 == d[v]) {//如果是可行流且v位于u的下一层
			int tmp = dfs(v, min(e[i].c, flow));
			//回退,修改路径上弧的容量
			flow -= tmp;
			e[i].c -= tmp;
			res += tmp;
			e[i ^ 1].c += tmp;	//修改反平行边
			if (flow == 0) break;	//流量达到上限
		}
	}
	if (res == 0) d[u] = -1;	//直接把u点拉黑
	return res;
}
int dinic() {
	int  res = 0;
	while (bfs()) res += dfs(S, INF);
	return res;
}
inline void ins(int u, int v, int c) { e[eid].v = v; e[eid].next = p[u]; e[eid].c = c; p[u] = eid++; }
inline void addedge(int u, int v, int c) { ins(u, v, c); ins(v, u, 0); }//插入弧及弧的反向剩余容量


inline int read() {
	int s = 0, f = 1; char c = getchar(); while (c<'0' || c>'9') { if (c == '-') f = -1; c = getchar(); }
	while (c >= '0'&&c <= '9') { s = s * 10 + c - '0'; c = getchar(); }
	return s*f;
}
inline void init() {
	eid = tot = 0;	//匹配里的人数
	memset(p, -1, sizeof(p));
	memset(ism, false, sizeof(ism));
}
int main() {
	t = read();
	while (t--) {
		init();
		n = read();
		S = 0, T = 2 * n + 1;	//新加一个超级源点与超级汇点 即可用网络流解决二分匹配问题
								//需要区分开来两个集合的人  随便加个n就好(不是很理解
		for (int i = 1; i <= n; i++) {
			if (read() == 1) {
				ism[i] = true;		//员工		有椅子
				addedge(i + n, T, 1);//和汇点连线
			}
			else {
				addedge(S, i, 1);//需要椅子
								 //不是员工 不管加不加班都要参与匹配 所以匹配人数++
				tot++;
			}
		}
		for (int i = 1; i <= n; i++) {
			if (read() == 0 && ism[i]) {							//加班  的员工
				addedge(S, i, 1);
				tot++;
			}
		}
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				if (read() == 1 || i == j) {
					addedge(i, j + n, 1);		//认识的人
				}
		//有两个集合:加班的公司员工和 朋友
		//如果同事A回家了 B加班 C来开会 那么可以B->A   C->B
		//然后emmm……二分图匹配
		printf(dinic() == tot ? "^_^\n" : "T_T\n");
	}
	//getchar();
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章