已知同一赛区的 N 只球队,现在知道他们现有得分,还有剩下的比赛场数(包括本赛区内的,和跨赛区的),以及赛区内部的对阵表,问 1 号球队能否获得赛区冠军(可以并列)。
大神的建图,我复述一遍,加深理解:
首先我们贪心选择 1 号球队剩下的比赛全部获胜,剩下的球队跨赛区的比赛全部输掉。如果这样,还是有球队的得分已经超过 1 号球队,那么就不用判断了,直接 “NO” 了。
如果一号球队还有戏的话,我们就开始建图:
从源点连到剩余球队的边(一号球队不用管了),权值为该球队和 1 号球队的胜场差。
如果球队 I 和球队 J 之间还有比赛,那么加一个点 P ,然后连三条边 I -> P ,J -> P,P -> 汇点,权值都为球队 I 和 球队 J 之间剩余的比赛场数,也就是mat[ i ][ j ]。
然后判断最大流是否等于所有和汇点相连的边的权值和,相等就是“YES”。
证明:源点发出的边的边权实质上是一个临界线,如果这些边满流,就意味着这些边所连接的球队和 1 号球队胜场数相等。而汇点处的边权表征的是剩余比赛场数,如果他们满流,意味着比赛已经打完。很显然,汇点处不满流,就表示至少有一组球队(有对阵关系的两只球队)得分和 1 号球队相等,而且还有比赛没有打完,那么无论那两只球队谁输谁赢,胜者得分必然超过 1 号球队。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define find_min(a,b) a<b?a:b
#define MAX 0x3f3f3f3f
using namespace std;
const int N = 30;
const int NN = 1000;
const int M = 30000;
struct Edge{
int s,e,v,next;
}edge[M];
int e_num,sum,head[NN],d[NN],sp,tp;
int n,score[N],remain[N],mat[N][N];
void AddEdge(int a,int b,int c){
edge[e_num].s=a; edge[e_num].e=b; edge[e_num].v=c;
edge[e_num].next=head[a]; head[a]=e_num++;
edge[e_num].s=b; edge[e_num].e=a; edge[e_num].v=0;
edge[e_num].next=head[b]; head[b]=e_num++;
}
int bfs(){
queue <int> q;
memset(d,-1,sizeof(d));
d[sp]=0;
q.push(sp);
while(!q.empty()){
int cur=q.front();
q.pop();
for(int i=head[cur];i!=-1;i=edge[i].next){
int u=edge[i].e;
if(d[u]==-1 && edge[i].v>0){//没有标记,且可行流大于0
d[u]=d[cur]+1;
q.push(u);
}
}
}
return d[tp] != -1;//汇点是否成功标号,也就是说是否找到增广路
}
int dfs(int a,int b){//a为起点
int r=0;
if(a==tp)return b;
for(int i=head[a];i!=-1 && r<b;i=edge[i].next){
int u=edge[i].e;
if(edge[i].v>0 && d[u]==d[a]+1){
int x=find_min(edge[i].v,b-r);
x=dfs(u,x);
r+=x;
edge[i].v-=x;
edge[i^1].v+=x;
}
}
if(!r)d[a]=-2;
return r;
}
void dinic(int sp,int tp){
int total=0,t;
while(bfs()){
while(t=dfs(sp,MAX))
total+=t;
}
if(sum==total)puts("YES");
else puts("NO");
}
int main()
{
int i,j;
while(~scanf("%d",&n))
{
memset(score,0,sizeof(score));
memset(mat,0,sizeof(mat));
memset(remain,0,sizeof(remain));
for(i=1;i<=n;i++)
scanf("%d",&score[i]);
for(i=1;i<=n;i++)
scanf("%d",&remain[i]);
for(i=1;i<=n;i++){
for(j=1;j<=n;j++)scanf("%d",&mat[i][j]);
}
score[1]+=remain[1];
int flag=1;
for(i=2;i<=n;i++){
if(score[i]>score[1]){
flag=0;break;
}
}
if(!flag){
puts("NO");continue;
}
int m=n;
int id[N][N];
for(i=2;i<=n;i++){
for(j=i+1;j<=n;j++)if(mat[i][j]>0)id[i][j]=++m;
}
sum=0; e_num=0;
memset(head,-1,sizeof(head));
sp=1; tp=m+1;
for(i=2;i<=n;i++){
for(j=i+1;j<=n;j++){
if(mat[i][j]>0){
sum+=mat[i][j];
AddEdge(i,id[i][j],mat[i][j]);
AddEdge(j,id[i][j],mat[i][j]);
AddEdge(id[i][j],tp,mat[i][j]);
}
}
}
for(i=2;i<=n;i++)
AddEdge(sp,i,score[1]-score[i]);
dinic(sp,tp);
}
return 0;
}