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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章