【问题描述】
下雨了,有F (1 <= F <= 200) 个牛棚,这F个牛棚之间有P (1 <= P <= 1500)条无向边(这些边长度会给出,牛每个单位时间走一个单位的距离)。
每个牛棚有两个值A和B(A表示一开始这个牛棚有A头牛,B表示下雨时该牛棚最多可以容纳B头牛躲雨)。问最少需要提前多少时间响下雨警告才能让所有牛在下雨前都能够找到可以遮雨的地方。
【输入格式】
第1行:两个整数: F and P
第2行至F+1行:下来F行,第i行两个整数A和B(AB的范围都是: 0..1000)描述第i个牛棚。
第F+2至F+P+1行: 下来P行,每行三个整数。X Y L,表示牛棚X和牛棚Y之间有一条边L( 1 <= L <= 1,000,000,000)
【输出格式】
输出一行,即为最短的时间,如果无法使得所有牛都能够有地方避雨,那么输出 “-1”.
Sample Input
3 4
7 2
0 4
2 6
1 2 40
3 2 70
2 3 90
1 3 120
Sample Output
110
输出解释:110是最短的时间。牛棚1安排4头牛到牛棚2,牛棚1再安排1头牛走到牛棚2,再走到牛棚3 避雨。这样最少的时间就是110。
分析
先用floyd求出的最短路,在二分答案,用最大流判段
建图: 源点–(W1)–>牛棚1–(W2)–>牛棚2–(W3)–>汇点
W1: 初始牛的数量
W2: INF(可以赋值为初始牛的数量)
W3: 目标牛的数量
PS:对于同一个牛棚,牛棚1–>牛棚2之间连边
代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#define IL inline
#define LL long long
#define INF 0x3f3f3f3f
#define open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);
#define close fclose(stdin); fclose(stdout);
using namespace std;
inline int read()
{
char c=getchar();
int sum=0,k=1;
for(;'0'>c || c>'9';c=getchar())
if(c=='-') k=-1;
for(;'0'<=c && c<='9';c=getchar()) sum=sum*10+c-'0';
return sum*k;
}
int S, T;
int n, m;
int num[205][2];
LL dis[205][205];
struct Edge
{
int to, nxt;
int cap, flow;
IL Edge(int v = 0, int nxt_ = 0, int c = 0, int f = 0)
{
to = v; nxt = nxt_; cap = c; flow = f;
}
}edge[81000];
int last[410];
int cnt;
IL void add(int u, int v, int c)
{
edge[cnt] = Edge(v, last[u], c, 0); last[u] = cnt++;
edge[cnt] = Edge(u, last[v], 0, 0); last[v] = cnt++;
}
int dep[410];
int cur[410];
IL LL min_(LL x, LL y) { return x < y? x: y; }
IL bool Bfs()
{
queue<int> Q;
memset(dep, 0, sizeof(dep));
dep[S] = 1;
Q.push(S);
for(int t = 1, u; t;)
{
u = Q.front(); Q.pop(); --t;
for(int i = last[u]; i != -1; i = edge[i].nxt)
if(!dep[edge[i].to] && edge[i].flow < edge[i].cap)
{
dep[edge[i].to] = dep[u] + 1;
Q.push(edge[i].to); ++t;
}
}
return dep[T];
}
IL int Dfs(int u, int a)
{
if(u == T || !a) return a;
int flow = 0, f;
for(int &i = cur[u]; i != -1; i = edge[i].nxt)
if(dep[edge[i].to] == dep[u] + 1 && (f = Dfs(edge[i].to, min_(a, edge[i].cap - edge[i].flow))) > 0)
{
edge[i].flow += f;
edge[i^1].flow -= f;
flow += f;
if(!(a -= f)) break;
}
return flow;
}
IL int check(LL mid)
{
memset(last, -1, sizeof(last)); cnt = 0;
for(int i = 1; i <= n; ++i)
{
add(S, i << 1, num[i][0]);
add(i << 1, i << 1 | 1, num[i][0]);
add(i << 1 | 1, T, num[i][1]);
}
for(int i = 1; i < n; ++i)
for(int j = i + 1; j <= n; ++j)
if(dis[i][j] <= mid)
{
add(i << 1, j << 1 | 1, num[i][0]);
add(j << 1, i << 1 | 1, num[j][0]);
}
int flow = 0;
for(; Bfs();)
{
for(int i = S; i <= T; ++i) cur[i] = last[i];
flow += Dfs(S, INF);
}
return flow;
}
int main()
{
open("1118");
int sum1 = 0, sum2 = 0;
n = read(); m = read();
S = 1; T = n + 1 << 1;
memset(dis, 0x3f, sizeof(dis));
for(int i = 1; i <= n; ++i)
{
num[i][0] = read();
num[i][1] = read();
sum1 += num[i][0];
sum2 += num[i][1];
}
if(sum2 < sum1) printf("-1\n"); else
{
for(int i = 1, x, y; i <= m; ++i)
{
x = read(); y = read();
dis[x][y] = dis[y][x] = min_(dis[x][y], read());
}
for(int k = 1; k <= n; ++k)
for(int i = 1; i < n; ++i)
if(k != i && dis[i][k] != INF)
for(int j = i + 1; j <= n; ++j)
if(k != j && i != j && dis[k][j] != INF)
dis[i][j] = dis[j][i] = min_(dis[i][k] + dis[k][j], dis[i][j]);
LL ans = -1;
for(LL l = 0, r = 2* (LL)1e11, mid; l <= r;)
{
mid = (l + r) >> 1;
if(check(mid) == sum1)
{
ans = mid;
r = mid -1;
}else
l = mid + 1;
}
printf("%lld\n",ans);
}
close;
return 0;
}