2022杭电多校十 1001-Winner Prediction(最大流)

题目链接:杭电多校10 - Virtual Judge

题目:

样例输入:

2
4 2 1
2 3 1
3 2 1
1 4
4 2 2
2 3 1
2 4 1
1 2
3 4

样例输出:

YES
NO

题意:一场比赛有n名参与者,一共进行了m1+m2场比赛,其中m1场比赛我们已经知道了结果,m2场比赛我们还不知道结果,赢得最多比赛的参与者赢得整个锦标赛,如果有多名参赛者并列第一,那么他们都会赢得锦标赛,现在问1号参与者有没有可能赢得比赛,如果有可能就输出YES,否则输出NO。

分析:我们用a[i]统计i号参与者赢得的比赛数,我们先对前m1场比赛结果已知的比赛进行统计结果,现在来贪心地考虑结果未知的m2场比赛,首先我们想让1号参与者尽可能获胜,所以有1号参与者参与的比赛我们都应该假设是1号参与者获胜,这是显然的,所以我们还可以处理完m2场比赛中有1号参与者的比赛,贪心处理完所有的比赛后1号参与者的最多获胜场次已经确定好了,如果这个时候有一名参与者的获胜场次大于1号参与者的获胜场次那么1号参与者肯定不会赢得比赛,这个时候可以直接输出NO,如果没有这样的参与者那我们再进行后续讨论,首先可以知道的一点是对于任意一个选手x在剩余结果未知且没有1号参与者参与的比赛中最多获胜的次数是a[1]-a[x],因为一旦超过这个数那么x号选手的获胜次数将会超过1号选手的获胜次数从而1号选手就不可能赢得比赛,这个时候我们就可以考虑建立一张网络流图:每场未进行的比赛在图中用一个点表示,源点向它连容量为1的边,它向它的两个参赛选手的对应点各自连容量为1的边,这代表两个参赛选手中只能有一个选手获胜。选手x的对应点向汇点连容量为a[1]-a[x]的边。计算该图最大流,若源点出发的边满流则答案为YES,否则为NO,由于图一共只有三层,若源点出发的边不满流说明我们可以通过增加第二层到第三层的边的流量而增大最大流,而边的权值代表可以允许该参赛者获胜的最大场次,一旦增加边权,则该参赛者获胜的最大次数将大于1号参赛者,所以就输出NO

细节见代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=2003,M=1e4+10;
int h[N],e[M],ne[M],w[M],idx;
int cur[N],d[N];
int n,m,s,t,a[N];
void add(int x,int y,int z)
{
	e[idx]=y;
	w[idx]=z;
	ne[idx]=h[x];
	h[x]=idx++;
}
bool bfs()
{
	memset(d,0,sizeof d);
	d[s]=1;
	queue<int>q;
	q.push(s);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(int i=h[x];i!=-1;i=ne[i])
		{
			int j=e[i];
			if(!d[j]&&w[i])
			{
				d[j]=d[x]+1;
				q.push(j);
				if(j==t) return true;
			}
		}
	}
	return false;
}
int dfs(int x,int mf)
{
	if(x==t) return mf;
	int ans=0;
	for(int i=cur[x];i!=-1;i=ne[i])
	{
		int j=e[i];
		cur[x]=i;
		if(d[j]==d[x]+1&&w[i])
		{
			int t=dfs(j,min(mf,w[i]));
			ans+=t;
			w[i]-=t;
			w[i^1]+=t;
			mf-=t;
			if(!mf) break;
		}
	}
	if(!ans) d[x]=0;
	return ans;
}
int Dinic()
{
	int ans=0;
	while(bfs())
	{
		memcpy(cur,h,sizeof h);
		ans+=dfs(s,0x3f3f3f3f);
	}
	return ans;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int m1,m2,tn;
		scanf("%d%d%d",&n,&m1,&m2);
		s=1;//源点为1
		t=n+m2+1;
		idx=0,tn=n;
		for(int i=1;i<=t;i++) h[i]=-1,a[i]=0;
		for(int i=1;i<=m1;i++)
		{
			int u,v,res;
			scanf("%d%d%d",&u,&v,&res);
			if(res) a[u]++;
			else a[v]++;
		}
		for(int i=1;i<=m2;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			if(u==1||v==1)	a[1]++;
			else
			{
				tn++;
				add(s,tn,1);add(tn,s,0);
				add(tn,u,1);add(u,tn,0);
				add(tn,v,1);add(v,tn,0);
			}
		}
		bool flag=true;
		for(int i=2;i<=n;i++)
		{
			if(a[i]>a[1])
			{
				flag=false;
				break;
			}
			add(i,t,a[1]-a[i]);
			add(t,i,0);
		}
		if(!flag)
		{
			puts("NO");
			continue;
		}
		if(Dinic()==tn-n)//源点满流 
			puts("YES");
		else 
			puts("NO");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值