直接求似乎不简单,由于题目求”最大值最小问题”,可以转化为二分答案来验证一个解是否成立.假设枚举的答案是x,那么所有人赢得的场次都<=x.而每场比赛最多只有一个人获胜,我们可以根据此信息,构图:
设置源点与每个人建边,每个人与它所参加的每场比赛建边,每场比赛与超级汇点建边并且流量设置为1.根据此图跑一遍最大流,判断是否为m即可.
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M=10005;
const int oo=1e9+5;
int head[M<<1],n,m,u[M],v[M],ec=0,tot;
int dis[M<<1],flw[M<<1];// dis表示层数
struct node{
int to,cap,nex;
}e[M*8];//边数 (n+3*m)*2
int Q[M<<2];
inline void rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<1)+(res<<3)+(c^48);
while(c=getchar(),c>=48);
}
void ins(int a,int b,int c){
e[ec]=(node){b,c,head[a]};
head[a]=ec++;
e[ec]=(node){a,0,head[b]};
head[b]=ec++;
}
bool BFS(){
memset(dis,-1,sizeof(dis));
int L=0,R=0,i,j,k;
dis[0]=0;
Q[R++]=0;
while(L<R){
int x=Q[L++];
if(x==tot)return true;
for(i=head[x];~i;i=e[i].nex){
int to=e[i].to,cap=e[i].cap;
if(dis[to]==-1&&cap){
dis[to]=dis[x]+1;
Q[R++]=to;
}
}
}
return false;
}
int dfs(int x,int flw){
if(x==tot)return flw;
int now=0;
for(int i=head[x];~i;i=e[i].nex){
int to=e[i].to,cap=e[i].cap;
if(dis[to]!=dis[x]+1||!cap)continue;
int f=dfs(to,min(flw,cap));
now+=f;
e[i].cap-=f;
e[i^1].cap+=f;
if(now==flw)break;
}
return now;
}
bool chk(int res){
memset(head,-1,sizeof(head));//初始化
ec=0;
int ans=0,i,j,k,a,b;
for(i=1;i<=n;i++)ins(0,i,res);
for(i=1;i<=m;i++){
ins(u[i],i+n,res);
ins(v[i],i+n,res);
ins(i+n,tot,1);
}//构图
while(BFS()){
do a=dfs(0,oo),ans+=a;
while(a!=0);
}
if(ans==m)return true;
return false;
}
int main(){
int l,r,i,j,k,ans;
rd(n);rd(m);tot=n+m+1;
for(i=1;i<=m;i++){
rd(v[i]);rd(u[i]);
}
l=(m+n-1)/n;r=m;
while(l<=r){
int mid=l+r>>1;
if(chk(mid)){
ans=mid;r=mid-1;
}else l=mid+1;
}
printf("%d\n",ans);
return 0;
}