这题是一道很好的最大流题目,题意是给定F个牛棚和P条路径,每条路径有一个权值,现在每个牛棚有一定的容量和牛数,因为牛棚牛数可能大于容量,所以要牛棚之间的牛要进行相互地移动,每移动一个距离就花费一单位的时间,求从开始移动到每头牛都移动到牛棚的最小时间
显然建模是从源点S连接每一个牛棚,容量为当前牛数,再从每一个点连接一条边到汇点T,容量为每一个牛棚的容量,求最短时间采用的是二分搜索算法,核心是枚举可能的时间然后求一次最大流,如果最大流结果刚好等于总牛数,那么这个时间显然符合题意就将他记录下来,我们要找到最小的时间,故将时间向小的地方调整,如果不等于说明对于当前时间有牛不能走到汇点,故将二分向大的地方调整,就这样一直二分下去,这里需要进行拆点,将某一个点i拆成i和i+n,然后在每一次二分开始前重建网络,如果i和i+n之间的最短路径小于等于当前值,说明在当前时间下可以走到汇点,也就是说这个牛棚可以被装满,那就连接一条边从i->i+n容量为无穷大,建完图后求一次最大流,然后加入二分进行比较,得到的最终结果就是题目所求
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
#define eps 10000000000000LL
const int eps1 = 100000000;
const int MAXE = 100000;
const int MAXN = 510;
struct node
{
int u, v, next;
int w;
}mapp[MAXE];
int id;
int head[MAXE];
void init()
{
id = 0;
memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int val)
{
mapp[id].u = u, mapp[id].v = v, mapp[id].w = val, mapp[id].next = head[u], head[u] = id ++;
mapp[id].u = v, mapp[id].v = u, mapp[id].w = 0, mapp[id].next = head[v], head[v] = id ++;
}
__int64 SP[MAXN][MAXN];
void floyd(int n)
{
for (int k = 1; k <= n; k ++){
for (int i = 1; i <= n; i ++){
for (int j = 1; j <= n; j ++){
if (SP[i][j] > SP[i][k] + SP[k][j] ){
SP[i][j] = SP[i][k] + SP[k][j];//可以自己到自己
}
}
}
}
}
int cur[MAXE], dist[MAXE], pre[MAXE], gap[MAXE];
int SAP(int s, int e, int n)
{
memcpy(cur, head, sizeof(head));
memset(dist, 0, sizeof(dist));
memset(gap, 0, sizeof(gap));
int u, v;
__int64 flow = 0;
int bottle = eps1;
gap[s] = n;
u = pre[s] = s;
bool flag = true;
while (dist[s] < n){
flag = false;
for (int &j = cur[u]; j != -1; j = mapp[j].next){
v = mapp[j].v;
if (mapp[j].w > 0 && dist[v]+1 == dist[u]){
flag = true;
if (mapp[j].w < bottle){
bottle = mapp[j].w;
}
pre[v] = u;
u = v;
if (u == e){
flow += bottle;
while (u != s){
u = pre[u];
mapp[cur[u]].w -= bottle;
mapp[cur[u]^1].w += bottle;
}
bottle = eps1;
}
break;
}
}
if (flag) continue;
__int64 mindis = n;
for (__int64 j = head[u]; j != -1; j = mapp[j].next){
v = mapp[j].v;
if (mapp[j].w > 0 && mindis > dist[v]){
mindis = dist[v], cur[u] = j;
}
}
if (!(--gap[dist[u]])){
break;
}
gap[dist[u] = mindis+1] ++;
u = pre[u];
}
return flow;
}
int a[MAXE], b[MAXE];
int sum;
__int64 ans;
void binaryS(int n, int m, int s, int e)
{
__int64 low = 0, high = eps;
__int64 mid;
while (low <= high){
mid = (low + high)/2;
init();
//依据最短路径重新建图
for (int i = 1; i <= n; i ++){
for (int j = 1; j <= n; j ++){
if (SP[i][j] <= mid){
addedge(i, j+n, eps1);
}
}
}
//增加单向边
for (int i = 1; i <= n; i ++){
addedge(s, i, a[i]);
addedge(i+n, e, b[i]);
}
int flow = SAP(s, e, 2*n+2);
if (flow == sum) {
high = mid-1;
ans = mid;
}
else{
low = mid+1;
}
}
}
int a1, b1;
__int64 c1;
int main()
{
int m, n;
while (scanf("%d%d", &n, &m) != EOF){
sum = 0;
int s = 2*n+1, e = 2*n+2;
for (int i = 1; i <= n; i ++){
scanf("%d%d", &a[i], &b[i]);
sum += a[i];
}
for (int i = 1; i <= n; i ++){
for (int j = 1; j <= n; j ++){
SP[i][j] = eps;
}
SP[i][i] = 0;
}
for (int i = 1; i <= m; i ++){
scanf("%d%d%I64d", &a1, &b1, &c1);
if (SP[a1][b1] > c1)
SP[b1][a1] = SP[a1][b1] = c1; //这里的路径应该是无向边
}
ans = -1;
floyd(n);
binaryS(n, m, s, e);
if (ans == eps){
printf("-1\n");
continue;
}
printf("%I64d\n", ans);
}
return 0;
}
另附此题数据如下: