sgu326 Perspective(最大流)

来源:互联网 发布:unity3d api 编辑:程序博客网 时间:2024/05/17 05:59

已知同一赛区的 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 0x3f3f3f3fusing 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){//没有标记,且可行流大于0d[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;}


原创粉丝点击