用dinic解決最大匹配 拆點分別表示椅子和人
1433: [ZJOI2009]假期的宿舍
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 3612 Solved: 1540
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
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;
}