【题意翻译】
给定一个\(V\) 个点\(E\) 条边的带权无向图,在图上有\(N\) 个人,第\(i\) 个人位于点\(x_ i\) ,一个人通过一条边需要花费这条边的边权的时间。
现在每个人可以*地走。求最短多少时间后满足结束后有人的节点数\(\geq K\)
\(N,V \leq 500\)
【题目分析】
首先发现V很小,直接用\(Floyd\)跑一遍全源最短路径。
然后考虑如何求时间,这个其实是图论中一个很经典的二分+图论的模型,那么我们可以用一个二分寻找\(mid\),连上所有长度\(\leq mid\)的边,用图论来\(check\)。
接下来就要选择图论使用的模型了。在一个图上,有一些人从一个点走向另一个点,很容易想到网络流中的最大流。由于每个点只要有人就行,那么让每个点向超级汇点\(t\)连一条容量为\(1\)的边,代表这个点对答案能产生\(1\)的流量,从超级源点\(s\)向每个点连一条容量为这个点人数的边,代表这个点能流出等同于这个点人数的流量,然后将\(\leq mid\)的边作为网络中的一条边,容量为\(inf\),跑最大流,判断是否\(\geq K\)。
\(tips:\)
- 建图的时候要拆点,拆成入口和出口,不然会出现流量分配的问题
- 每次check记得memset
- 重边要取最小值
愉快的贴代码时间
#include<bits/stdc++.h>
#define inf 1e15
using namespace std;
int read(){
int w=0,h=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')h=-h;ch=getchar();}
while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
return w*h;
}
int n,m,s,t,K,cnt,sum;
int cow[610],barn[610];
int floyd[610][610];
struct Dinic{
struct node{
int next,to,cap,flow;
}edge[3000010];
int head[10010],num;
int cur[10010];
int dist[10010];
queue<int>q;
void add(int u,int v,int cap,int flow){
edge[++num].next=head[u];
edge[num].to=v;
edge[num].cap=cap;
edge[num].flow=flow;
head[u]=num;
return;
}
void Add(int u,int v,int cap){
add(u,v,cap,0);
add(v,u,0,0);
return;
}
bool bfs(){
memset(dist,0,sizeof(dist));
dist[s]=1;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;
if(!dist[v]&&edge[i].cap>edge[i].flow){
dist[v]=dist[u]+1;
q.push(v);
}
}
}
return dist[t];
}
int dfs(int u,int flow){
if(u==t)return flow;
int fl=0;
for(int i=cur[u];i;i=edge[i].next){
cur[u]=i;
int v=edge[i].to;
if(dist[u]+1==dist[v]&&edge[i].cap>edge[i].flow){
int p=dfs(v,min(flow,edge[i].cap-edge[i].flow));
if(p){
edge[i].flow+=p;
edge[i^1].flow-=p;
flow-=p;
fl+=p;
if(!flow)break;
}
}
}
if(!fl)dist[u]=0;
return fl;
}
int solve(){
int flow=0;
while(bfs()){
memcpy(cur,head,sizeof(cur));
int p;
while(p=dfs(s,1e9))flow+=p;
}
return flow;
}
void memst(){
memset(head,0,sizeof(head));
num=1;
}
bool check(int x){
memst();
for(int i=1;i<=n;i++)Add(i+n,t,1);
for(int i=1;i<=n;i++){
if(cow[i])Add(s,i,cow[i]);
for(int j=1;j<=n;j++)
if(floyd[i][j]<=x)
Add(i,j+n,1e9);
}
return solve()>=K;
}
}ans;
signed main(){
n=read();m=read();cnt=read();K=read();
s=0;t=n+n+2;
memset(floyd,0x3f,sizeof(floyd));
for(int i=1;i<=cnt;i++)cow[read()]++;
for(int i=1;i<=n;i++)floyd[i][i]=0;
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();
floyd[u][v]=floyd[v][u]=min(floyd[u][v],w);
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
floyd[i][j]=min(floyd[i][j],floyd[i][k]+floyd[k][j]);
int l=0,r=1731311,res=-1;
while(l<=r){
int mid=l+r>>1;
if(ans.check(mid))r=mid-1,res=mid;
else l=mid+1;
}
printf("%d\n",res);
return 0;
}