BZOJ1305 [CQOI2009]dance跳舞【二分图匹配 / 网络流】

[ C Q O I 2009 ] d a n c e 跳 舞 [CQOI2009]dance跳舞 [CQOI2009]dance

Description:

  • 一次舞会有n个男孩和n个女孩。每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞。每个男孩都不会和同一个女孩跳两首(或更多)舞曲。有一些男孩女孩相互喜欢,而其他相互不喜欢(不会“单向喜欢”)。每个男孩最多只愿意和k个不喜欢的女孩跳舞,而每个女孩也最多只愿意和k个不喜欢的男孩跳舞。给出每对男孩女孩是否相互喜欢的信息,舞会最多能有几首舞曲?

Input Format:

  • 第一行包含两个整数n和k。以下n行每行包含n个字符,其中第i行第j个字符为’Y’当且仅当男孩i和女孩j相互喜欢。

Output Format:

  • 仅一个数,即舞曲数目的最大值。

Sample Input:

  • 3 0
    YYY
    YYY
    YYY

Sample Output:

  • 3

TJ:

二分图匹配
每次找图中的最大匹配
匹配数等于n时答案+1,然后把当前匹配的边全删去继续二分图匹配。
匹配不到n就跳出,最后答案为min(ans + k, n)
考虑每次匹配,此次匹配的方案和下次能否构成最大匹配没有影响,假设此次匹配中
A->A’
B->B’
改成
A->B’
B->A’
那么下次匹配时还是可以匹配A和B
所以当前选择的匹配和下一次能否构成最大匹配无影响

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 55;
char s[MAXN];
int n,k,G[MAXN][MAXN],match[MAXN],vis[MAXN];
bool dfs(int u){
	vis[u] = 1;
	for(int i = 1; i <= n; i++){
		if(!G[u][i]) continue;
		if(match[i]==-1||(!vis[match[i]]&&dfs(match[i]))){
			match[i] = u;
			return true;
		}		
	}
	return false;
}
int BMatch(){
	int tot = 0;
	memset(match,-1,sizeof(match));
	for(int i = 1; i <= n; i++){
		memset(vis,0,sizeof(vis));
		if(dfs(i)) tot++;
	}
	return tot;
}
int main(){
	#ifndef ONLINE_JUDGE
	freopen("1305/10.in","r",stdin);
	#endif
	memset(G,0,sizeof(G));
	scanf("%d %d",&n,&k);
	for(int i = 1; i <= n; i++) {
		scanf("%s",s+1);
		for(int j = 1; j <= n; j++) G[i][j] = s[j]=='Y';
	}
	int ans = 0;
	while(true){
		int tp = BMatch();
		if(tp==n) ans++;
		else break;
		for(int i = 1; i <= n; i++) G[match[i]][i] = 0;
	}
	ans = min(n,ans+k);
	printf("%d\n",ans);
	#ifndef ONLINE_JUDGE
	freopen("1305/10.out","r",stdin);
	scanf("%d",&ans);
	printf("%d\n",ans);
	#endif
	return 0;
}

网络流拆点

#include<bits/stdc++.h>
using namespace std;
#define S 0
#define T n*6+1
const int INF = 0x3f3f3f3f;
const int MAXN = 111;

struct EDGE{
	int to,cap,rev;
	EDGE(){}
	EDGE(int to,int cap,int rev){
		this->to = to;
		this->cap = cap;
		this->rev = rev; 
	}
};
int n,k,rk[MAXN<<2],iter[MAXN<<2];
int mp[MAXN][MAXN];
vector<EDGE> G[MAXN<<2];
char s[MAXN];
void Add_edge(int u,int v,int cap){
	G[u].push_back(EDGE(v,cap,(int)G[v].size()));
	G[v].push_back(EDGE(u,0,(int)G[u].size()-1));
}
void init(){ for(int i = 0; i < (MAXN<<2); i++) G[i].clear(); }
void rebuild(int m){
	for(int i = 1; i <= n; i++){
		Add_edge(S,i,m);
		Add_edge(i,i+n*2,INF);
		Add_edge(i,i+n*4,k);
	}
	for(int i = n + 1; i <= n*2 ;i++){
		Add_edge(i,T,m);
		Add_edge(i+n*2,i,INF);
		Add_edge(i+n*4,i,k);
	}
	for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) {
		if(mp[i][j]) Add_edge(i+n*2,j+n*3,1);
		else Add_edge(i+n*4,j+n*5,1);
	}
}
bool BFS(){
	memset(iter,0,sizeof(iter));
	memset(rk,0,sizeof(rk));
	rk[S] = 1;
	queue<int> que;
	que.push(S);
	while(!que.empty()){
		int now = que.front();
		que.pop();
		for(int i = 0; i <(int)G[now].size(); i++){
			EDGE e = G[now][i];
			if(!e.cap||rk[e.to]) continue;
			rk[e.to] = rk[now] + 1;
			que.push(e.to);
		}
	}
	return rk[T]!=0;
}
int DFS(int u,int f){
	if(u==T) return f;
	for(int i = 0; i < (int)G[u].size(); i++){
		EDGE &e = G[u][i];
		if(!e.cap||rk[e.to]!=rk[u]+1) continue;
		int d = DFS(e.to,min(f,e.cap));
		if(d){
			e.cap -= d;
			G[e.to][e.rev].cap += d;
			return d;
		}
	}
	return 0;
}
int Dinic(){
	int flow = 0;
	while(BFS()){
		int d = DFS(S,INF);
		while(d){
			flow+=d;
			d = DFS(S,INF);
		}
	}
	return flow;
}
bool check(int m){
	init();
	rebuild(m);
	return Dinic()==m*n;
}
int pan(){
	int l = k,r = n;
	while(l<=r){
		int m = (l+r)>>1;
		if(check(m)) l = m + 1;
		else r = m - 1;
	}
	return r;
}
int main(){
	#ifndef ONLINE_JUDGE
	freopen("1305/6.in","r",stdin);
	#endif
	scanf("%d %d",&n,&k);
	for(int i = 1; i <= n; i++){
		scanf("%s", s+1);
		for(int j = 1; j <= n; j++){
			if(s[j]=='Y') mp[i][j] = 1;
			else mp[i][j] = 0;
		}
	}
	printf("%d\n",pan());
	#ifndef ONLINE_JUDGE
	freopen("1305/6.out","r",stdin);
	int x;
	scanf("%d",&x);
	printf("%d\n",x);
	#endif
	return 0;
}
``
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值