比较好的网络流题。
Description
给出每个队目前的胜场,已经还未进行的比赛情况,问有哪些队伍可能会成为全场胜场最多的队伍。
Solution
这个建图有最小权闭合子图的感觉。
首先,考虑什么时候可以胜场最多,显然是如果一个队伍剩下的所有比赛都赢了,然后获胜的场次是全场最多时,他才有可能是全场胜场最多的。
所以我们就可以遍历每个队伍,依次让他们取得所有的胜利,然后判断一下他们是否可以成为胜场最多的队伍。
因为每两个队伍进行一场比赛,所以我们可以将比赛和队伍分别作为点来建图。
具体方法如下,对于每个队伍:
-
首先统计当前队伍的最大胜场(已胜和可能胜的);
-
源点向对于每组比赛连容量为比赛场次的边;
-
每组比赛向参加的两支队伍分别连容量无穷大的边;
-
每支队伍向汇点连容量为自己的当前胜场数与当前的最大胜场差值的边。
然后跑出的每组最大流,如果等于当前还未进行的总场次,那么说明当前这支队伍可能成为全场胜场最多的队伍。
然后有几个地方:
-
如果统计完一个队伍的胜场之后仍比另一个队伍的已胜场次少,那么就不用跑网络流了;
-
如果当前边的容量是 0 的话可以不用建这条边,否则会出奇奇怪怪的错误,比如超时等等;
-
总场次如果在输入中处理,记得除以二;
-
每次循环记得初始化。
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define rr register
#define maxn 5010
#define INF 0x3f3f3f3f
//#define int long long
using namespace std;
int n,tot=1,s,t,cnt,all;
int a[maxn][maxn],id[maxn][maxn];
int Dis[maxn],head[maxn],cur[maxn];
struct node{int win,defeat;}b[maxn];
struct edge{int fr,to,dis,nxt;}e[maxn*maxn];
inline int read(){
rr int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
return s*w;
}
void add(int fr,int to,int dis){
e[++tot]=(edge){fr,to,dis,head[fr]};head[fr]=tot;
e[++tot]=(edge){to,fr,0,head[to]};head[to]=tot;
}
bool bfs(){
memset(Dis,-1,sizeof Dis);
queue<int> q;q.push(s);
Dis[s]=0;cur[s]=head[s];
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nxt){
int to=e[i].to;
if(e[i].dis&&Dis[to]==-1){
q.push(to);
Dis[to]=Dis[u]+1;
cur[to]=head[to];
if(to==t) return true;
}
}
}
return false;
}
int dfs(int u,int limit){
if(u==t) return limit;int flow=0;
for(int i=cur[u];i&&flow<limit;i=e[i].nxt){
int to=e[i].to;cur[u]=i;
if(e[i].dis&&Dis[to]==Dis[u]+1){
int f=dfs(to,min(e[i].dis,limit-flow));
if(!f)Dis[to]=-1;e[i].dis-=f;e[i^1].dis+=f;
flow+=f;
}
}
return flow;
}
int dinic(){
int Maxflow=0,flow=0;
while(bfs())
while(flow=dfs(s,INF))
Maxflow+=flow;
return Maxflow;
}
void clear(){
tot=1;
memset(cur,0,sizeof cur);
memset(head,0,sizeof head);
}
int main(){
n=read();s=n*(n+3);t=s+1;
for(rr int i=1;i<=n;i++)
b[i].win=read(),b[i].defeat=read();
for(rr int i=1;i<=n;i++)for(rr int j=1;j<=n;j++)
all+=(a[i][j]=read()),id[i][j]=++cnt;
for(rr int i=1,tMxs;i<=n;i++){
tMxs=b[i].win;bool flag=0;
for(rr int j=1;j<=n;j++) tMxs+=a[i][j];
for(rr int j=1;j<=n;j++) if(tMxs<b[j].win){flag=1;break;}
if(flag) continue;
for(rr int j=1;j<=n;j++){
for(rr int k=j+1;k<=n;k++){
if(a[k][j]) add(s,id[j][k]+n,a[k][j]);
add(id[j][k]+n,j,INF),add(id[j][k]+n,k,INF);
}
if(tMxs-b[j].win>=0) add(j,t,tMxs-b[j].win);
}
int ans=dinic();
if(ans==all/2) printf("%d ",i);clear();
}
return 0;
}