P4012 深海机器人问题 [最小费用最大流问题]

深海机器人问题

中文题干
大意就是,一些边,让那个多个机器人去走。机器人的起点和终点都可能不相同,每条边的权值都只能被加一次,问最大的权值是多少。

第一次遇到两个点间可以建两条边的网络流图,长见识长见识。。。
在这里插入图片描述
来源:洛谷题解

这就是具体的建边图,超级厉害呃- -。
将源点与各个起点相互连接,边的流量是出发的机器人数;
将汇点与各个重点相互连接,边的流量是到达要到达这个点的机器人数;
以上两类边都没有费用。
而中间的边,是根据题意,
两个点之间有一条流量为1的 费用为题给边权值,还有一条流量为INF,费用为0的边;
我们要求最大费用,所以我们就把权值变成负数,最后变回来就可以了。
这两条边的意义是,我们根据spfa,会先走权值是负的那条边,而走过之后,流量清空,说明这条边的权值就不会再加上去了。
之后走,就只会走流量是INF的那条边了。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>

#define len 200010
#define MAXN 500
#define INF 0x3f3f3f3f
using namespace std;

struct edge{
    int u, v, c, nxt, f;
};

edge e[len];
int head[MAXN], hcnt;
int inq[MAXN], dis[MAXN], pre[len];
int S, T;

void Init (){
    memset(head, -1, sizeof (head));
    hcnt = 0;
}

void adde(int u, int v, int f,int c){
    e[hcnt].u = u; e[hcnt].v = v; e[hcnt].c = c; e[hcnt].f = f;
    e[hcnt].nxt = head[u]; head[u] = hcnt ++;
    e[hcnt].u = v; e[hcnt].v = u; e[hcnt].c = -c; e[hcnt].f = 0;
    e[hcnt].nxt = head[v]; head[v] = hcnt ++;
}

bool spfa(){
    queue <int> q;
    memset(dis, INF, sizeof (dis));
    memset(pre, -1, sizeof (pre));
    memset(inq, 0, sizeof (inq));
    dis[S] = 0; q.push(S);
    inq[S] ++;
    while (!q.empty()){
        int h = q.front(); q.pop();
        
        for (int i=head[h]; ~i; i=e[i].nxt){
            int v = e[i].v;
            if (dis[v] > dis[h] + e[i].c && e[i].f > 0){
                dis[v] = dis[h] + e[i].c;
                pre[v] = i;
                if (!inq[v]){
                    inq[v] ++;
                    q.push(v);
                }
            }
        }
        inq[h] = 0;
    }
    if (pre[T] == -1) return false;
    else return true;
}

int dfs(){
    int res = 0;
    
    while (spfa()){
        int d = INF;
        for (int i=pre[T]; ~i; i=pre[e[i].u])
            if (d > e[i].f) d = e[i].f;
        
        for (int i=pre[T]; ~i; i=pre[e[i].u]){
            res += d * e[i].c;
            e[i].f -= d;
            e[i^1].f += d;
        }
    }
    
    return res;
}

int main(){
    int a, b;
    scanf("%d%d", &a, &b);
    Init();
    int P, Q;
    scanf("%d%d", &P, &Q);
    S = 0; T = (P + 1) * (Q + 1) + 1;

    for (int i=0; i<=P; i++){
        for (int j=1; j<=Q; j++){
            int c; scanf("%d", &c);
            adde(i*(Q+1)+j, i*(Q+1)+j+1, INF, 0);
            adde(i*(Q+1)+j, i*(Q+1)+j+1, 1, -c);
//            cout << "!!!:" << i*(Q+1)+j << " " << i*(Q+1)+j+1 << endl;
        }
    }

    for (int i=0; i<=Q; i++){
        for (int j=1; j<=P; j++){
            int c; scanf("%d", &c);
            adde(i+1+(j-1)*(Q+1), i+1+j*(Q+1), INF, 0);
            adde(i+1+(j-1)*(Q+1), i+1+j*(Q+1), 1, -c);
//            cout << "!!!:" << i+1+(j-1)*(Q+1) << " " << i+1+j*(Q+1) << endl;
        }
    }

    for (int i=0; i<a; i++){
        int k, x, y; scanf("%d%d%d", &k, &x, &y);
        adde(S, x*(Q+1)+y+1, k, 0);
//        cout << "!!!:" << x*(Q+1)+y+1 << endl;
    }

    for (int i=0; i<b; i++){
        int r, x, y; scanf("%d%d%d", &r, &x, &y);
        adde(x*(Q+1)+y+1, T, r, 0);
//        cout << x*(Q+1)+y+1 << endl;
    }
    
    int ans = dfs();
    printf ("%d\n", -ans);
    
    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值