bzoj1532 [POI2005]Kos-Dicing 最大流+二分

这题又wa又T的,终于在y学长的帮助下A了,accept令人如此感动。。。。

不煽情了,开始题解

求瓶颈问题显然是二分的,那么我们可以二分最大值。

至于最大流建边,由超级源点向每场比赛建容量为1的边,由每场比赛向参赛者建容量为1的边,再由参赛者向汇点建容量为二分出的答案x的边,最大流与比赛场数相等时二分小答案

附代码(只写了三次最大流,代码略丑):

<strong>#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
int n,m,nowp;
int head[20010];
int cur[20010];
int er[10001][2];
int pos=1;
struct edge
{
	int to,c,next;
}e[1000001];
void add(int a,int b,int c)
{
	pos++;
	e[pos].to=b,e[pos].c=c;
	e[pos].next=head[a];
	head[a]=pos;
}
int dis[100001];
queue<int>Q;
bool bfs()
{
	int i,j;
	for(int i=1;i<=n+m+2;i++)dis[i]=-1;
	dis[n+m+1]=0;
	while(!Q.empty()) Q.pop();
	Q.push(n+m+1);
	while(!Q.empty())
		{
			int u=Q.front();
			Q.pop();
			for(int i=head[u];i;i=e[i].next)
				{
					int v=e[i].to;
					if(e[i].c&&dis[v]<0)
						{
							dis[v]=dis[u]+1;
							if(v==m+n+2)return 1;
							Q.push(v);
						}
				}
		}
	return 0;
}
int find(int x,int low)
{
	int a=0;
	int tmp=low;
	if(x==n+m+2)return low;
	for(int i=cur[x];i;i=e[i].next)
		{
			int v=e[i].to;
			if(dis[v]==dis[x]+1&&e[i].c)
				{
					a=find(v,min(tmp,e[i].c));
					e[i].c-=a;
					e[i^1].c+=a;
					tmp-=a;
					if(e[i].c)cur[x]=i;
					if(!tmp)return low;
				}
		}
	if(low==tmp)dis[x]=-1;
	return low-tmp;
}
bool check(int x)
{
	int ans=0;
	while(bfs())
		{
			for(int i=1;i<=n+m+2;i++)
				cur[i]=head[i];
		//	printf("*\n");
			ans+=find(n+m+1,0x7fffffff);
		}
	return ans==m;
}
int ans;
void build(int mid)
{
	pos=1;
	memset(head,0,sizeof(head));
	int s=n+m+1,t=s+1;
	for(int i=1;i<=m;i++)
		{
			add(s,n+i,1);
			add(n+i,s,0);
		}
	for(int i=1;i<=m;i++)
		{
			add(n+i,er[i][0],1);
			add(er[i][0],n+i,0);
			add(n+i,er[i][1],1);
			add(er[i][1],n+i,0);
		}
	for(int i=1;i<=n;i++)
		{
			add(i,t,mid);
			add(t,i,0);
		}
}
void solve()
{
	int l=1,r=m;
	while(l<=r)
		{
			int mid=(l+r)>>1;
			build(mid);
			if(check(mid))
				{
					ans=mid;
					r=mid-1;
				}
			else l=mid+1;
		}
}
int a,b;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        {
			scanf("%d%d",&a,&b);
			er[i][0]=a,er[i][1]=b;
	    }
	solve();
	printf("%d\n",ans);
}
</strong>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值