pku2391:
感觉这是一个不错的最大流的题目。
题目大意:有n个农场和m条连接这些农场的路径,牛们走过某条边时都需要一个时间(也就是每条边有一个权值)。牛是很怕下雨的,每个农场有一个牛棚,用来避雨。每个农场两个参数——一个是当前这个农场里牛的个数,另一个是这个农场的牛棚可以容纳的牛的个数。每头牛均可以走到其他农场去避雨,现在暴风雨就要来了,问牛们最少需要走多长时间才能保证每头牛都不会被雨淋到,如果无解则输出-1.
解法:看完题目之后大致有两种思路,一种是贪心——每次寻找最短路,类似于最小费用流每次寻找最小费用路。注意此处和最小费用流是不同的,最小费用流最优化的值是:sigma(f(i,j)*cost(i,j)),而这个题目最优化的值是:让所有增广路径中最长的那一条最短。求解结果几乎扯不上关系。。。一般这种最优化问题,只要解满足单调性,使用二分来逼近是比较好的方式。而且此题中除了与源和汇关联的边,所有的容量可视为INF。所以可以先用floyd预处理一下,然后二分最大增广路径长度,将每个节点拆成两个,建成一个二分图,两端节点分别代表增广路径中除源和汇之外的起点和终点。然后运行最大流判定是否满流就可以了。
贪心的方法就不说了,那种做法是错误的- -!有反例,我实践过了- -!
#include <iostream>
#include <cstdio>
#include <cstring>
usingnamespace std;
typedef longlong llg;
constint N =210;
constint inf =0x1fffffff;
int n, m, s, t, sum, cows[N], limit[N], queue[N+N], lab[N+N], r[N+N][N+N];
llg dis[N][N];
void floyd()
{
int i, j, k;
for(k =1; k <= n; k++)
for(i =1; i <= n; i++)
if(i!=k && dis[i][k]!=-1)
for(j =1; j <= n; j++)
if(i!=j && j!=k && dis[k][j]!=-1)
{
if(dis[i][j]==-1|| dis[i][j]>dis[i][k]+dis[k][j])
dis[i][j] = dis[i][k] + dis[k][j];
}
}
bool bfs(int s, int t)
{
int head, tail, i, u;
memset(lab, -1, sizeof(lab));
lab[s] = head = tail =0;
queue[0] = s;
while(head <= tail)
{
u = queue[head++];
for(i =1; i <= t; i++)
if(r[u][i]>0&& lab[i]==-1)
{
lab[i] = lab[u] +1;
queue[++tail] = i;
}
if(lab[t] !=-1) returntrue;
}
returnfalse;
}
int dinicDfs(int delta, int u)
{
int i, sum =0, tmp;
if(u == t) return delta;
else
{
for(i =1; i <= t; i++)
if(lab[i]==lab[u]+1&& r[u][i]>0)
{
tmp = dinicDfs(min(delta, r[u][i]), i);
sum += tmp;
delta -= tmp;
r[u][i] -= tmp;
r[i][u] += tmp;
}
return sum;
}
}
int maxFlow(int s, int t)
{
int ans =0;
while(bfs(s, t))
ans += dinicDfs(inf, s);
return ans;
}
int main()
{
int i, j, x, y, z, tmp;
llg ll, rr, mid, ans;
scanf("%d%d", &n, &m);
memset(dis, -1, sizeof(dis));
sum =0;
rr =0;
for(i =1; i <= n; i++)
{
scanf("%d%d", cows+i, limit+i);
sum += cows[i];
dis[i][i] =0;
}
for(i =0; i < m; i++)
{
scanf("%d%d%d", &x, &y, &z);
if(dis[x][y]==-1|| dis[x][y]>z)
dis[x][y] = dis[y][x] = z;
rr += (llg)z;
}
floyd();
ll =0;
s =0;
t = n+n+1;
ans =-1;
while(ll <= rr)
{
mid = (ll+rr) >>1;
memset(r, 0, sizeof(r));
for(i =1; i <= n; i++)
{
r[s][i] = cows[i];
r[i+n][t] = limit[i];
}
for(i =1; i <= n; i++)
for(j =1; j <= n; j++)
if(dis[i][j]!=-1&& dis[i][j]<=mid)
r[i][j+n] = inf;
tmp = maxFlow(s, t);
if(tmp == sum)
{
ans = mid;
rr = mid-1;
}
else ll = mid+1;
}
printf("%I64d\n", ans);
return0;
}