Luogu P1401 城市(二分+网络流)
题意
题目描述
N(2<=n<=200)个城市,M(1<=m<=40000)条无向边,你要找T(1<=T<=200)条从城市1到城市N的路,使得最长的边的长度最小,边不能重复用。
输入输出格式
输入格式:
第1行三个整数N,M,T用空格隔开。
第2行到P+1行,每行包括三个整数Ai,Bi,Li表示城市Ai到城市Bi之间有一条长度为Li的道路。
输出格式:
输出只有一行,包含一个整数,即经过的这些道路中最长的路的最小长度。
输入输出样例
输入样例#1:
7 9 2
1 2 2
2 3 5
3 7 5
1 4 1
4 3 1
4 5 7
5 7 1
1 6 3
6 7 3
输出样例#1:
5
思路
这道题可以用并查集做。 --alecli
每条边最多走一遍,不就相当于对图跑一遍网络流,流量必须大于等于\(T\)吗?所以我们可以二分答案,对原图跑网络流,判断流量是否满足要求。
AC代码
#include<bits/stdc++.h>
using namespace std;
int n,m,t,ans,L=INT_MAX,R,cur[205],dep[205];
int cnt=1,top[205],to[160005],len[160005],cap[160005],rcap[160005],nex[160005];
int read()
{
int re=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
return re;
}
bool bfs(int lim)
{
memset(cur,0,sizeof cur);
memset(dep,0,sizeof dep);
cur[1]=top[1],dep[1]=1;
queue<int>Q;
Q.push(1);
while(!Q.empty())
{
int now=Q.front();Q.pop();
for(int i=cur[now];i;i=nex[i])
if(!dep[to[i]]&&len[i]<=lim&&cap[i])
{
cur[to[i]]=top[to[i]],dep[to[i]]=dep[now]+1;
Q.push(to[i]);
}
}
return dep[n]!=0;
}
int dfs(int now,int flow,int lim)
{
if(now==n) return flow;
int re=0;
for(int &i=cur[now];i;i=nex[i])
if(dep[to[i]]==dep[now]+1&&len[i]<=lim&&cap[i])
{
int lzq=dfs(to[i],min(flow,cap[i]),lim);
if(lzq)
{
re+=lzq,flow-=lzq;
cap[i]-=lzq,cap[i^1]+=lzq;
if(!flow) break;
}
}
return re;
}
bool check(int lim)
{
int tmp=0;
for(int i=1;i<=cnt;i++) cap[i]=rcap[i];
while(bfs(lim)) tmp+=dfs(1,INT_MAX,lim);
return tmp>=t;
}
int main()
{
n=read(),m=read(),t=read();
while(m--)
{
int x=read(),y=read(),z=read();
to[++cnt]=y,len[cnt]=z,cap[cnt]=rcap[cnt]=1,nex[cnt]=top[x],top[x]=cnt;
to[++cnt]=x,len[cnt]=z,cap[cnt]=rcap[cnt]=0,nex[cnt]=top[y],top[y]=cnt;
to[++cnt]=y,len[cnt]=z,cap[cnt]=rcap[cnt]=0,nex[cnt]=top[x],top[x]=cnt;
to[++cnt]=x,len[cnt]=z,cap[cnt]=rcap[cnt]=1,nex[cnt]=top[y],top[y]=cnt;
L=min(L,z),R=max(R,z);
}
while(L<=R)
{
int mid=(L+R)>>1;
if(check(mid)) ans=mid,R=mid-1;
else L=mid+1;
}
printf("%d",ans);
return 0;
}