[bzoj 1305&1433]最大流练习题

1305: [CQOI2009]dance跳舞

Time Limit: 5 Sec   Memory Limit: 162 MB
Submit: 3330   Solved: 1409
[ Submit][ Status][ Discuss]

Description

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

Input

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

Output

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

Sample Input

3 0
YYY
YYY
YYY

Sample Output

3

HINT

N<=50 K<=30

Source

这两题的难点就难在建图 建图过程详见代码注释

对于dfs的写法稍有改变 如按照原模板会超时。。。

借鉴hzwer的写法


//http://www.lydsy.com/JudgeOnline/problem.php?id=1305
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define inf 0x7fffffff
using namespace std;
int n,k,cnt=1;int start=0,end=1001;
int ans;
int mp[1005][1005],head[1005],d[1005];
struct edge{int to,w,next;}e[500001];
int mid;
void ini(int x,int y,int z){
	e[++cnt].to=y;e[cnt].w=z;e[cnt].next=head[x];head[x]=cnt;
}
void insert(int x,int y,int z){
	ini(x,y,z);ini(y,x,0);
}
queue<int>q;
bool bfs(){
	memset(d,-1,sizeof(d));
	d[start]=0;
	q.push(start);
	while(!q.empty()){
		int k=q.front();q.pop();
		for(int i=head[k];i;i=e[i].next){
			int kk=e[i].to;
			if(d[kk]==-1&&e[i].w>0){
				d[kk]=d[k]+1;
				q.push(kk);
			}
		}
	}
	if(d[end]==-1) return false;
	return true;
}
int dfs(int x,int f)
{
    if(x==end)return f;
    int w,used=0,i;
    i=head[x];
    while(i)
    {
            if(e[i].w&&d[e[i].to]==d[x]+1)
            {
                w=f-used;
                w=dfs(e[i].to,min(w,e[i].w));
                e[i].w-=w;
                e[i^1].w+=w;
                used+=w;
                if(used==f)return f;
            }
            i=e[i].next;
    }
    if(!used) d[x]=-1;
    return used;
}
int dinic(){
	while(bfs()){
		int a;
		if(a=dfs(0,inf))
		ans+=a;
	}
}
void build(){
	cnt=1;
	memset(head,0,sizeof(head));
	for(int i=1;i<=n;i++) insert(start,i,mid);//超级源和男的入点连
	for(int i=1;i<=n;i++) insert(i,i+500,k);//男的入点连出点
	for(int i=1;i<=n;i++) insert(i+n+500,i+n,k);//女 出点连入点
	for(int i=1;i<=n;i++) insert(i+n,end,mid);//女的入点和超级汇连
	for(int i=1;i<=n;i++)
    	for(int j=1;j<=n;j++)
          if(mp[i][j]) insert(i,n+j,1);//喜欢就连入点
          else insert(i+500,n+j+500,1);//否则连出点
}
int main(){
	//freopen("rand.txt","r",stdin);
	scanf("%d%d",&n,&k);
	char s[100];
	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;
		}
	}
	int l=0,r=50;
	int mx=0;
	while(l<=r){
		mid=(l+r)>>1;
        build();
		ans=0;dinic();
		if(ans>=n*mid){
			mx=mid;
			l=mid+1;
		} 
		else r=mid-1;
	}
	printf("%d",mx);
	return 0;
}

1433: [ZJOI2009]假期的宿舍

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 2927   Solved: 1237
[ Submit][ Status][ Discuss]

Description

Input

Output

Sample Input

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

Sample Output

ˆ ˆ

HINT

对于30% 的数据满足1 ≤ n ≤ 12。
对于100% 的数据满足1 ≤ n ≤ 50,1 ≤ T ≤ 20。

Source

//http://www.lydsy.com/JudgeOnline/problem.php?id=1433
//拆点 把人和床分开 1-n是人 1+n-2*n是床 
//床和汇点连 源点连要床的人 每个人和能睡的床连 
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue> 
#define inf 0x7fffff
using namespace std;
int d[105],head[105],n,cnt=1,school[105],ans,tot;
int start=0,end=101;
struct edge{int to,next,w;}e[50005];
void ini(int x,int y,int z){
	e[++cnt].to=y;e[cnt].w=z;e[cnt].next=head[x];head[x]=cnt;
}
void insert(int x,int y,int z){
	ini(x,y,z);ini(y,x,0);
}
queue<int>q;
bool bfs(){
	q.push(start);
	memset(d,-1,sizeof(d));
	d[start]=0;
	while(!q.empty()){
		int k=q.front();q.pop();
		for(int i=head[k];i;i=e[i].next)
		{
			int kk=e[i].to;
			if(d[kk]==-1&&e[i].w>0)
			{
				d[kk]=d[k]+1;
				q.push(kk);
			}
		}
	}
	if(d[end]==-1) return false;
	return true;
}
int dfs(int x,int f){
	if(x==end) return f;
	int a,used=0;
	for(int i=head[x];i;i=e[i].next){
		int k=e[i].to;
		if(d[k]==d[x]+1&&e[i].w>0){
			a=f-used;
			a=dfs(k,min(a,e[i].w));
			e[i].w-=a;
			e[i^1].w+=a;
			used+=a;
			if(used==f) return f; 
		}
	}
	if(!used) d[x]=-1;
	return used;
} 
void dinic(){
	while(bfs())
		ans+=dfs(start,inf);
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		ans=tot=0;cnt=1;memset(head,0,sizeof(head));
		scanf("%d",&n);
		int temp;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&school[i]);
			if(temp==1)
			insert(i+n,end,1);//住校代表有床 床连汇点 
		}	
		int x;
		for(int i=1;i<=n;i++){
			scanf("%d",&x);
			if((school[i]&&!x)||!school[i]){//源点连需要床的人 
				insert(start,i,1);tot++;
			}
		}
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			{
				scanf("%d",&x);
				if(x||i==j) insert(i,j+n,1);//人连能睡的床 
			}
		dinic();
		if(ans==tot)puts("^_^");
        else puts("T_T");
	}
	return 0;
} 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值