poj2391 Ombrophobic Bovines

这题是一道很好的最大流题目,题意是给定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;   
}

 另附此题数据如下:

http://ace.delos.com/MAR05_4.htm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值