The Battle of Guandu

41 篇文章 0 订阅

这里写图片描述
这里写图片描述



题意:给定n个村庄,可以理解每个村庄有无限多人,曹操向每一个村庄要一个人到固定的战场需要花费cost[i]的代价,同时会有一个人加入袁绍(敌方)的某个固定的战场。现在每个战场有一个重要值(0,1,2),0代表无关紧要,1代表不能输,2代表一定要赢,问曹操赢的最小花费。



差分约束的思想
其实对于每一个村庄,话费代价使得一个人到自己 i 战场一个人到敌方 j 战场,相当于花费这个代价,从自己对应的 j 战场中借走一个人到自己的 i 战场中。那么对于价值为0的战场无关紧要,借走多少人都可以;对于价值为1的战场,被借走多少人就一定要借回来多少人;对于价值为3的战场,最终一定要从别的地方借来1个人(因为借1个人代价绝对比借多个人代价少)。对于上文提到的 i , j ,连一条又 j 到 i 长度为对应的cost 的边,于是就相当于每一个价值为3的战场要找一个价值为0的借1个人,那么最小花费就是最短路。最后把所有价值为3的战场的最小花费加起来就是答案了。



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <queue>
using namespace std;

const long long INF = 1ll<<50;
const int maxn = 200000;
int a[maxn], b[maxn], cost[maxn], val[maxn];
long long dist[maxn], ans;
int tar[maxn], c[maxn], nextt[maxn], last[maxn], tot;
int n, m, t, k, num, o;
bool flag[maxn], all;

struct Node {
    long long s, t, val;
    bool operator<(Node &other) {
        if (dist[s]+val < dist[other.s]+other.val) return true;
        return false;
    }
}heap[maxn];

void up(int x) {
    while (x > 1 && heap[x] < heap[x/2]) {
        swap(heap[x], heap[x/2]);
        x = x/2;
    }
}

void down(int x) {
    while (x*2 <= num) {
        o = x*2;
        if (o+1 <= num && heap[o+1] < heap[o]) o++;
        if (heap[o] < heap[x]) {
            swap(heap[o], heap[x]);
            x = o;
        } else break;
    }
}

void insert(int x, int y, int z) {
    tot++;
    tar[tot] = y;
    c[tot] = z;
    nextt[tot] = last[x];
    last[x] = tot;
}

int main() {
    int T;
    scanf("%d", &T);
    for (int cases = 1; cases <= T; cases++) {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
        for (int i = 1; i <= n; i++) scanf("%d", &cost[i]);
        for (int i = 1; i <= m; i++) scanf("%d", &val[i]);
        tot = 0;
        memset(last, 0, sizeof(last));
        for (int i = 1; i <= n; i++) if (a[i] != b[i] && a[i] != 0) {
            insert(b[i], a[i], cost[i]);
        }
        num = 0;
        for (int i = 1; i <= m; i++)
            if (val[i] == 0) dist[i] = 0;
            else dist[i] = INF;

        for (int i = 1; i <= m; i++) if (val[i] == 0) {
            k = last[i];
            while (k > 0) {
                num++;
                heap[num].s = i;
                heap[num].t = tar[k];
                heap[num].val = c[k];
                up(num);
                k = nextt[k];
            }
        }

        while (1) {
            while (num > 0 && dist[heap[1].s]+heap[1].val >= dist[heap[1].t]) {
                swap(heap[1], heap[num]);
                num--;
                down(1);
            }
            if (num == 0) break;
            dist[heap[1].t] = dist[heap[1].s]+heap[1].val;
            k = last[heap[1].t];
            while (k != 0) {
                num++;
                heap[num].s = heap[1].t;
                heap[num].t = tar[k];
                heap[num].val = c[k];
                up(num);
                k = nextt[k];
            }
            swap(heap[1], heap[num]);
            num--;
            down(1);
        }

        ans = 0;
        all = true;
        for (int i = 1; i <= m; i++) if (val[i] == 2) {
            if (dist[i] == INF) {
                all = false; break;
            }
            ans += dist[i];
        }
        printf("Case #%d: ", cases);
        if (all) printf("%lld\n", ans);
        else printf("-1\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值