题目链接:哆啦A梦传送门
题意:一个n个点无向图,每个点有Ai头牛,每个点的牛棚能容纳Bi头牛,有m条边,每条边有一个权值为w,表示u点到v点需用多少时间,现在要使得所有牛都跑进牛棚,问:最快多久能跑进去,如果不能全部跑进去,输出-1。
题解:
二分时间:每次二分跑一次dinic,如若满流,则此时间满足条件。
建模:给每个点i拆点为 (i,i1)。
原点s到每个点i建边,权值为Ai。
点i1到汇点t建边,权值为Bi。
如果u点到v点的最短时间小于等于mid,此两点建边,权值为INF。
最后跑下dinic,如果满流就满足情况。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
typedef long long LL;
#define INF 0x3f3f3f3f
struct edge{
int from,to,w,next;
}e[200010];
int cnt;
int dep[500],head[510];
//queue<int> que;
void init(){
memset(head,-1,sizeof(head));
cnt=0;
}
void add(int u,int v,int w)
{
e[cnt].from=u;
e[cnt].to=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt++;
e[cnt].from=v;
e[cnt].to=u;
e[cnt].w=0;
e[cnt].next=head[v];
head[v]=cnt++;
}
int bfs(int s,int t)
{
// while(!que.empty()) que.pop();
queue<int> que;
memset(dep,-1,sizeof(dep));
dep[s]=0;
que.push(s);
while(!que.empty()){
int u=que.front();
que.pop();
// if(u==t) return 1;
for(int i=head[u];i!=-1;i=e[i].next){
int w=e[i].w;
int v=e[i].to;
if(w&&dep[v]==-1){
dep[v]=dep[u]+1;
que.push(v);
if(v==t) return 1;
}
}
}
return 0;
}
int dfs(int u,int mi,int t){
if(u==t||mi==0) return mi;
int tmp;
int ret=0;
// for(int v=1;v<=t;v++){
for(int i=head[u];i!=-1;i=e[i].next){
int w=e[i].w;
int v=e[i].to;
if(w&&dep[v]==dep[u]+1&&mi-ret>0){ ///mi-ret>0少一次递归,优化
tmp=dfs(v,min(mi-ret,w),t) ;
if(!tmp) dep[v]=0;
e[i].w-=tmp;
e[i^1].w+=tmp;
ret+=tmp;
if(ret==mi) return ret;
}
}
return ret;
}
int dinic(int s,int t)
{
int ans=0;
while(bfs(s,t)){
ans+=dfs(s,INF,t);
}
return ans;
}
LL dist[210][210];///任意两点的距离
int node1[210],node2[210]; ///分别表示每点的牛,该点牛棚所能容纳的牛数量
int main()
{
int n,m;
scanf("%d%d",&n,&m);
int sum=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&node1[i],&node2[i]);
sum+=node1[i];
}
///初始化
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i==j) dist[i][j]=0;
else dist[i][j]=-1;
}
LL ma=-1;
for(int i=1;i<=m;i++)
{
int u,v;
LL w;
scanf("%d%d%lld",&u,&v,&w);
if(dist[u][v]==-1||dist[u][v]>w){
dist[u][v]=dist[v][u]=w;
ma=max(ma,w);
}
}
///floyb
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(dist[i][k]==-1||dist[k][j]==-1) continue;
if(dist[i][j]==-1||dist[i][k]+dist[k][j]<dist[i][j]){
dist[i][j]=dist[i][k]+dist[k][j];
ma=max(ma,dist[i][j]);
}
}
LL l=0,r=ma+1;
LL ans=-1;
while(l<=r)
{
///二分时间
LL mid=(l+r)>>1;
int s=0,t=2*n+1;
init();
for(int i=1;i<=n;i++)
{
add(s,i,node1[i]);
add(i+n,t,node2[i]);
add(i,i+n,INF);
for(int j=1;j<=n;j++)
{
///任意两点的时间要小于二分的时间,加入这条边
if(i!=j&&dist[i][j]!=-1&&dist[i][j]<=mid){
add(i,j+n,INF);
}
}
}
///如果满流,保存该二分值
if(dinic(s,t)==sum){
ans=mid;
r=mid-1;
}
else l=mid+1;
}
printf("%lld\n",ans);
return 0;
}