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;
}