sgu 326

 

 http://acm.sgu.ru/problem.php?contest=0&problem=326

题意:两个赛区,每个赛区N个球队(标号1-N),现在已知一个赛区中每只球队赢得的场数和与每只球队还有多少场比赛,现在问你是否有可能让第一支球队成为分区第一。

网络流

不同赛区之间的比赛,如果是1的话让1赢,如果是2-N的话,则让它输,这是显然的因为另一赛区的成绩不影响1所在赛区的排名。对于赛区内的比赛和1比的肯定是要让1赢得,对于2-N之间的比赛,假设u,v之间有k场比赛,那么u,v的胜场和肯定是k,所以可以用网络求解。

对于矩阵中每个元素g[i][j],以这个元素为点向i,j连边,容量为inf,加源和汇,源向矩阵中的元素连边容量为g[i][j],每只球队向汇连边,容量为第1支球队的得分。源向每支球队连边容量为每支球队的已有得分。最后判断最大流是否满流即可。

#include<stdio.h>
#include<string.h>

#define N 450
const int inf=0x3f3f3f3f;
int a[25],b[25],g[25][25];
int map[N][N],dis[N],cnt[N]; 
int pre[N],now[N],low[N];

int sap(int n,int s,int t) //顶点为0-n
{
     int i,j,k,flow=inf,min,ans=0;
     bool flag;
	 memset(dis,0,sizeof(dis));
	 memset(cnt,0,sizeof(cnt));
	 memset(pre,0,sizeof(pre));
     for(i=0;i<=n;i++) now[i]=0;
     cnt[0]=n+1;
     i=s;
     while(dis[s]<=n)
	 {
		 low[i]=flow;
		 flag=false;
         for(j=now[i];j<=n;j++)
			 if(map[i][j]>0&&dis[i]==dis[j]+1)
			 {
				 now[i]=j;flag=true;
				 if(map[i][j]<flow) flow=map[i][j];
				 pre[j]=i;i=j;
				 if(i==t)
				 {     
					 ans+=flow;  
					 while(i!=s)
					 {      
						 k=pre[i];
                         map[k][i]-=flow;
                         map[i][k]+=flow;
						 i=k;
                     }
                     flow=inf;
				 }
				 break;
			 }
             if(flag) continue;
			 if(--cnt[dis[i]]==0) break;
			 for(min=n+1,j=0;j<=n;j++)
				 if(map[i][j]>0&&dis[j]<min)
				 {
					 min=dis[j];
					 now[i]=j;
				 }     
             dis[i]=min+1;
             cnt[dis[i]]++;
             if(i!=s)  //修改顶标后回溯
			 {
				 i=pre[i];
				 flow=low[i];
			 }
       }
	 return ans;
}
int main()
{
	int n,i,j,k,c,sum;
	while(scanf("%d",&n)!=EOF)
	{
		memset(map,0,sizeof(map));
		c=0;
		for(i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			c+=a[i];
		}
		for(i=1;i<=n;i++)
			scanf("%d",&b[i]);
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++)
				scanf("%d",&g[i][j]);
		for(j=2,sum=0;j<=n;j++)
		{
			sum+=g[1][j];
			c+=g[1][j];
		}
		k=1;
		map[k][n*n+1]=inf;
		map[0][k++]=sum;
		for(i=2;i<=n;i++)
			for(j=i+1;j<=n;j++)
			{
				map[k][n*n+i]=inf;	
				map[k][n*n+j]=inf;
				map[0][k]=g[i][j];
				c+=g[i][j];
				k++;
			}
		for(i=1;i<=n;i++)
		{
			map[n*n+i][n*n+n+1]=a[1]+b[1];
			map[0][n*n+i]=a[i];
		}
		int ans=sap(n*n+n+1,0,n*n+n+1);
		//printf("%d %d\n",ans,c);
		if(ans==c)
			printf("YES\n");
		else
			printf("NO\n");
	}
	return 0;
}
		

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值