Question
给你个点,条边。每个点都有一个容量限制和当前存在的量,每条边都有一个所需时间。
求当所有点的量都符合限制是的最少需要时间。
Solution
我们先用求出两两点之间的最短时间。
然后二分答案,对于时间的边就加,而后跑一遍网络流看看是否全都能流完即可。
加边具体操作:
设起始点为,终点为。
1.向第个点连一条的边
2.第个点向连一条的边
3.对于时间的原边,第个点向第个点连一条的边
Code
#include<cstdio>
#include<cstring>
#define N 210
#define ll long long
using namespace std;
struct node{int v,fr,flow;}e[N*N*20];
int n,m,a[N],b[N],tail[N<<1],cnt;
int S=0,T,num=0,dis[N<<1],gap[N<<1];
ll l=0,r=0,mid,ans=-1,fl[N][N];
inline int read()
{
int x=0; char c=getchar();
while (c<'0' || c>'9') c=getchar();
while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
inline ll min(ll x,ll y) {return x<y ? x:y;}
inline void add(int u,int v,int flow)
{
e[++cnt]=(node){v,tail[u],flow}; tail[u]=cnt;
e[++cnt]=(node){u,tail[v],0}; tail[v]=cnt;
}
int dfs(int x,int hav)
{
if (x==T) return hav;
int gone=0;
for (int p=tail[x],v,can;p;p=e[p].fr)
{
v=e[p].v;
if (dis[v]+1!=dis[x] || !e[p].flow) continue;
can=dfs(v,min(hav-gone,e[p].flow));
e[p].flow-=can,e[p^1].flow+=can,gone+=can;
if (gone==hav) return gone;
}
gap[dis[x]]--;
if (!gap[dis[x]]) dis[S]=T;
gap[++dis[x]]++;
return gone;
}
bool check(ll mid)
{
cnt=1;
memset(tail,0,sizeof(tail));
memset(dis,0,sizeof(dis));
memset(gap,0,sizeof(gap));
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
if (fl[i][j]<=mid)
add(i,n+j,a[i]),add(j,n+i,a[j]);
for (int i=1;i<=n;i++)
add(S,i,a[i]),add(n+i,T,b[i]),add(i,n+i,a[i]);
int s=0;
while (dis[S]<T) s+=dfs(S,num);
if (s==num) return 1;
else return 0;
}
int main()
{
freopen("rain.in","r",stdin);
freopen("rain.out","w",stdout);
n=read(),m=read();T=n+n+1;
for (int i=1;i<=n;i++)
a[i]=read(),b[i]=read(),num+=a[i];
memset(fl,60,sizeof(fl));
for (int i=1,u,v,lg;i<=m;i++)
{
u=read(),v=read(),lg=read();r+=lg;
fl[v][u]=fl[u][v]=min(fl[u][v],lg);
}
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
fl[j][i]=fl[i][j]=min(fl[i][j],fl[i][k]+fl[k][j]);
while (l<=r)
{
mid=l+r>>1;
if (check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%lld
",ans);
return 0;
}