洛谷P6768 Ombrophobic Bovines 发抖的牛

题目描述

FJ 的牛们非常害怕淋雨,那会使他们瑟瑟发抖。他们打算安装一个下雨报警器,并且安排了一个撤退计划。他们需要计算最少的让所有牛进入雨棚的时间。
牛们在农场的 FF 个田地上吃草。有 PP 条双向路连接着这些田地。路很宽,无限量的牛可以通过。田地上有雨棚,雨棚有一定的容量,牛们可以瞬间从这块田地进入这块田地上的雨棚。
请计算最少的时间,让每只牛都进入雨棚。

输入格式

第 11 行:两个整数 FF 和 PP;
第 22 到 F+1F+1 行:第 i+1i+1 行有两个整数描述第 ii 个田地,第一个表示田地上的牛数,第二个表示田地上的雨棚容量。两个整数都在 00 和 10001000 之间。
第 F+2F+2 到 F+P+1F+P+1 行:每行三个整数描述一条路,分别是起点终点,及通过这条路所需的时间(在 11 和 10^9109 之间)。

输出格式

一个整数,表示最少的时间。如果无法使牛们全部进入雨棚,输出 -1−1。

输入输出样例

输入 #1复制

3 4
7 2
0 4
2 6
1 2 40
3 2 70
2 3 90
1 3 120

输出 #1复制

110

说明/提示

对于 100\%100% 的数据:1\le F\le 2001≤F≤200,1\le P\le 15001≤P≤1500。

上代码:

#include <bits/stdc++.h>
using namespace std;
#define M 100010
#define N 1010
#define ll long long
const ll inf = (ll)1e18;  // 一定要够大 

template <class T>
inline void read(T& a){
    T x = 0, s = 1;
    char c = getchar();
    while(!isdigit(c)){
        if(c == '-') s = -1;
        c = getchar();
    }
    while(isdigit(c)){
        x = x * 10 + (c ^ '0');
        c = getchar();
    }
    a = x * s;
    return ;
}

struct node{
    int v, next;
    ll w;
    
    public:
        node(int v = 0, ll w = 0, int next = -1){  // 方便初始化为 -1 
            this -> v = v;
            this -> w = w;
            this -> next = next;
            return ;
        }
        
    inline void clean(){
        this -> v = 0;
        this -> w = 0;
        this -> next = -1;
        return ;
    }
        
}t[M << 1];
int f[N];
ll dis[N][N];
int n, m;
int suma = 0, sumb = 0;  // 奶牛总数和雨棚总容量 
int a[N], b[N];
int s, ht; // 起点和终点 

int bian = -1;
inline void add(int u, int v, ll w){
    t[++bian] = node(v, w, f[u]), f[u] = bian;
    t[++bian] = node(u, 0, f[v]), f[v] = bian;  // 反向虚边 
    return ;
}

struct Max_Flow{    // 最大流 
    int deth[N];
    int cur[N];    

    bool bfs(){
        queue <int> q;    
        memset(deth, 0, sizeof(deth));
        deth[s] = 1; 
        q.push(s);
        while(!q.empty()){
            int now = q.front(); q.pop();
            for(int i = f[now]; ~i; i = t[i].next){
                int v = t[i].v;
                if(!deth[v] && t[i].w){
                    deth[v] = deth[now] + 1;
                    q.push(v);
                }
            }
        }
        return deth[ht] != 0;
    }
    
    ll dfs(ll now,ll dist){
        if(now == ht || !dist)return dist;
        for(int& i = cur[now]; ~i; i = t[i].next){
            ll v = t[i].v;
            if(deth[v] == deth[now] + 1 && t[i].w != 0){
                ll di = dfs(v, min(dist, t[i].w));
                if(di > 0){
                    t[i].w -= di;
                    t[i^1].w += di;
                    return di;
                }
            }
        }    
        return 0; 
    }
    
    ll Dicnic(){
        ll sum = 0;
        while(bfs()){
            memcpy(cur, f, sizeof(cur));
            while(ll temp = dfs(s, inf))
                sum += temp;        
        }
        return sum; 
    }
    
} T;

inline void clean(){
    memset(f, -1, sizeof(f));
    for(int i = 1; i <= bian; i++)
        t[i].clean();
    bian = -1;
    return ;
}

bool judge(ll mid, int sum){
    clean();                        // 记得清空 
    for(int i = 1; i <= n; i++){
        add(s, i, a[i]);
        add(i + n, ht, b[i]);
        for(int j = 1; j <= n; j++){
            if(i == j || dis[i][j] <= mid)   // 本身就在这块农田或者距离在当前限制之内 
                add(i, j + n, inf);
        }
    }
    return T.Dicnic() == sum;
}

int main(){

    read(n), read(m);
    for(int i = 1; i <= n; i++){
        read(a[i]), read(b[i]);
        suma += a[i], sumb += b[i];  // 牛的总数, 容量的总数 
    }
    if(suma > sumb){  // 特判,如果根本放不下就直接去世 
        printf("-1");
        return 0;  
    }
    
    s = n * 2 + 1, ht = n * 2 + 2;
    memset(dis, 0x3f, sizeof(dis));    
    for(int i = 1; i <= m; i++){
        ll x, y, w;
        read(x), read(y), read(w);
        dis[x][y] = dis[y][x] = min(dis[x][y], w);
    }
    for(int k = 1; k <= n; k++)    // Floyd 最短路 
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
                
    ll l = 0, r = 0;
    ll ans = -1;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            if(i != j)  r = max(r, dis[i][j]);
            
    while(l <= r){      // 二分答案 
        ll mid = l + r >> 1;
        if(judge(mid, suma)) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    printf("%lld\n", ans < inf ? ans : -1);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值