HDU 4966 GGS-DDU

43 篇文章 0 订阅

题意:

你有n个课程  每个课程有一个规定的毕业学分  修学分有m种方式  每种方式要求先修到x课程x'学分以上才能花费money去修y课程并且将学分修到y'  问  最少花费多少可以毕业

思路:

一开始想费用流  建完图发现一个问题解决不掉  那就是  一条边如果流过多次怎样才能让费用只计算一次  所以换思路

我们知道  为了应付“ 学分修到y' ”这个条件  高层学分一定要“覆盖”低层学分  那么就想到以每个学分为一个点  高层向低层连边  费用为0  同时修学分的方式是输入的  直接建边  费用为输入费用  一开始只能走到0学分  所以超级源点向所有0学分连边  费用0  那么此时我们想要的是从源点花最小费用走到所有点  这就是有向图最小生成树  即最小树形图

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define M 610
#define inf 2000000000

int n, m, ans, tot, S;
int head[M], id[M], in[M], pre[M], vis[M], node[M][M], s[M];
struct ed {
    int u, v, w, next;
} ed[M * M];

int ZL(int root, int V, int E) {
    int ret = 0;
    for (;;) {
        for (int i = 0; i < V; i++) {
            id[i] = -1;
            vis[i] = -1;
            in[i] = inf;
        }
        for (int i = 0; i < E; i++) {
            int u = ed[i].u;
            int v = ed[i].v;
            if (ed[i].w < in[v] && u != v) {
                pre[v] = u;
                in[v] = ed[i].w;
            }
        }
        for (int i = 0; i < V; i++) {
            if (i == root)
                continue;
            if (in[i] == inf)
                return -1;
        }
        int cnt = 0;
        in[root] = 0;
        for (int i = 0; i < V; i++) {
            ret += in[i];
            int v = i;
            while (vis[v] != i && id[v] == -1 && v != root) {
                vis[v] = i;
                v = pre[v];
            }
            if (v != root && id[v] == -1) {
                for (int u = pre[v]; u != v; u = pre[u])
                    id[u] = cnt;
                id[v] = cnt++;
            }
        }
        if (cnt == 0)
            break;
        for (int i = 0; i < V; i++)
            if (id[i] == -1)
                id[i] = cnt++;
        for (int i = 0; i < E; i++) {
            int u = ed[i].u;
            int v = ed[i].v;
            ed[i].u = id[u];
            ed[i].v = id[v];
            if (id[u] != id[v])
                ed[i].w -= in[v];
        }
        V = cnt;
        root = id[root];
    }
    return ret;
}

void add(int U, int V, int W) {
    ed[tot].u = U;
    ed[tot].v = V;
    ed[tot].w = W;
    ed[tot].next = head[U];
    head[U] = tot++;
}

int main() {
    int i, j, k, u, v, tmp;
    while (scanf("%d%d", &n, &m) != EOF) {
        if (!n && !m)
            break;
        k = 0;
        for (i = 1; i <= n; i++) {
            scanf("%d", &s[i]);
            for (j = 0; j <= s[i]; j++)
                node[i][j] = k++;
        }
        S = k++;
        tot = 0;
        memset(head, -1, sizeof(head));
        for (i = 1; i <= n; i++) {
            add(S, node[i][0], 0);
            for (j = 1; j <= s[i]; j++)
                add(node[i][j], node[i][j - 1], 0);
        }
        for (i = 1; i <= m; i++) {
            scanf("%d%d", &u, &v);
            tmp = node[u][v];
            scanf("%d%d", &u, &v);
            v = node[u][v];
            u = tmp;
            scanf("%d", &tmp);
            add(u, v, tmp);
        }
        ans = ZL(S, k, tot);
        printf("%d\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值