<题目链接>
题目大意:
给定一个有$n$个顶点和$m$条边的无向图,点$i$ 处有$A_i$头牛,点$i$ 处的牛棚能容纳$B_i$头牛,每条边有一个时间花费$t_i$(表示从一个端点走到另一个端点所需要的时间),求一个最短时间T使得在T时间内所有的牛都能进到某一牛棚里去。$(1 <= N <= 200, 1 <= M <= 1500,0 <= A_i <= 10^3, 0 <= B_i <= 10^3, 1 <= Dij <= 10^9)$。
解题分析:
很明显要二分答案,二分枚举最短的时间,然后用最大流去进行验证。将每个点$i$拆成两个点$i$和$i+n$,然后就是对于每次枚举的答案,进行重新建图,然后跑最大流,如果最大流等于所有牛的数量,则说明所有的牛都有牛棚能够停留,符合条件。建图的方法就是:超级源点向所有的点$i$连一条边,容量为i点初始牛的数量,所有点$i+n$向汇点连一条边,容量为该点牛的容量。因为点的数量很少,floyd预处理出任意两点之间的最短距离,然后对于每次枚举的时间T,最短距离小于等于T的边就加入网络,建好图之后,跑一遍最大流,进行判断。
#include <cstdio> #include <cstring> #include <vector> #include <queue> #include <algorithm> using namespace std; typedef long long ll; const int N = 505 ; const ll INF = 1e18; int n,m,st,ed; int num[N],capa[N]; ll g[N][N],fullFlow; template<typename T> inline void read(T&x){ x=0;int f=1;char c=getchar(); while(c<'0' || c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0' && c<='9'){ x=x*10+c-'0';c=getchar(); } x*=f; } struct Dinic { struct edge{ int from,to;ll cap,flow; }; vector<edge>es; vector<int>G[N]; bool vis[N]; int g[N],iter[N]; void init(int n){ for(int i=0; i<=n+10; i++)G[i].clear(); es.clear(); } void addedge(int from,int to,ll cap){ es.push_back((edge){from,to,cap,0}); es.push_back((edge){to,from,0,0}); int x=es.size(); G[from].push_back(x-2); G[to].push_back(x-1); } bool BFS(int s,int t){ memset(vis,0,sizeof(vis)); queue <int> q; vis[s]=1; g[s]=0; q.push(s); while(!q.empty()){ int u=q.front();q.pop(); for(int i=0; i<G[u].size(); i++){ edge &e=es[G[u][i]]; if(!vis[e.to]&&e.cap>e.flow){ vis[e.to]=1; g[e.to]=g[u]+1; q.push(e.to); } } } return vis[t]; } int DFS(int u,int t,ll f){ if(u==t||f==0)return f; int numflow=0,d; for(int &i=iter[u]; i<G[u].size(); i++){ edge &e=es[G[u][i]]; if(g[u]+1==g[e.to]&&(d=DFS(e.to,t,min(f,e.cap-e.flow)))>0){ e.flow+=d; es[G[u][i]^1].flow-=d; numflow+=d; f-=d; if(f==0)break; } } return numflow; } int Maxflow(int s,int t){ int flow=0; while(BFS(s,t)){ memset(iter,0,sizeof(iter)); int d=0; while(d=DFS(s,t,INF))flow+=d; } return flow; } }dinic; bool check(ll T){ //最长的时间限制 dinic.init(N); for(int i=1;i<=n;i++){ dinic.addedge(st,i,num[i]); //源点向每个点连一条边,容量为这个点的牛的数量 dinic.addedge(i+n,ed,capa[i]); //每个点拆点之后的点向汇点连一条边,容量为这个点能够容纳牛的数量 } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(g[i][j]<=T) //将符合条件的边加上 dinic.addedge(i,j+n,INF); return dinic.Maxflow(st,ed) == fullFlow; //判断是否满流,即是否所有的牛都有牛棚住 } inline void Floyd(){ //floyd求出任意两点之间的最短距离 for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(g[i][j]>g[i][k]+g[k][j]) g[i][j]=g[i][k]+g[k][j]; } inline void Init(){ fullFlow = 0; st=0,ed=2*n+1; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) g[i][j]=(i==j)?0:INF; //初始化任意两点之间的距离 for(int i=1;i<=n;i++){ read(num[i]);read(capa[i]); fullFlow +=num[i]; //记录所有牛的数量 } for(int i=1;i<=m;i++){ int u,v;ll w; read(u);read(v);read(w); g[u][v]=g[v][u]=min(g[u][v],w); } Floyd(); } int main(){ while(~scanf("%d%d",&n,&m)){ Init(); ll l=0,r=0;//二分的上下界 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(g[i][j]<INF) r=max(r,g[i][j]); //找到时间的上界 ll ans=-1; //二分答案 while(l<=r){ ll mid=(l+r)>>1; if(check(mid))ans=mid,r=mid-1; else l=mid+1; } printf("%lld\n",ans); } }