hdu4966 GGS-DDU

hdu4966 GGS-DDU

\(n\) 个课程,每种课程有 \(a_i\) 级,一开始你每种课程都为 \(0\) 级,有 \(m\) 个升级方案:\((x,\ l1,\ y,\ l2,\ c)\) ,若你课程 \(x\) 已达到 \(l1\) 级,那么你可以花费 \(c\) 的价格,使得课程 \(y\) 达到 \(l2\) 级。求最小花费使得所有课程满级。

\(n\leq50,\ m\leq2\times10^3,\ a_i\leq500\)

最小树形图


将每个课程拆为 \(a_i\) 个点,分别表示此课程等级为 \(0\cdots a_i\) 。建一个虚拟根节点,连向所有等级为 \(0\) 的节点,边权为 \(0\) 。升级方案可以连边课程 \(x\)\(l1\cdots a_x\) 到课程 \(y\)\(l2\) ,边权为 \(c\) ,再从所有等级为 \(k\) 的节点向等级为 \(k-1\) 的节点连边,权值为 \(0\) ,接着跑最小树形图就吼辣

时间复杂度 \(O(m\sum a_i)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 2.5e4 + 10, maxm = 1e6 + 10;
int n, m, k, a[60], mp[60][510], val[maxn], vis[maxn], pre[maxn], tid[maxn];
struct edges {
  int u, v, w;
  edges(int x = 0, int y = 0, int z = 0) : u(x), v(y), w(z) {}
} e[maxm];

int edmonds() {
  int ans = 0;
  while (1) {
    memset(vis, 0, sizeof vis);
    memset(tid, 0, sizeof tid);
    memset(val, 0x3f, sizeof val);
    for (int i = 1; i <= m; i++) {
      int u = e[i].u, v = e[i].v;
      if (u != v && e[i].w < val[v]) {
        val[v] = e[i].w, pre[v] = u;
      }
    }
    for (int i = 1; i < n; i++) {
      if (val[i] > 1e9) return -1;
    }
    int tot = 0;
    for (int i = 1; i < n; i++) {
      int u = i;
      ans += val[i];
      while (u < n && !tid[u] && vis[u] != i) {
        vis[u] = i, u = pre[u];
      }
      if (u < n && !tid[u]) {
        tid[u] = ++tot;
        for (int v = pre[u]; u != v; v = pre[v]) {
          tid[v] = tot;
        }
      }
    }
    if (!tot) break;
    for (int i = 1; i <= n; i++) {
      if (!tid[i]) tid[i] = ++tot;
    }
    for (int i = 1; i <= m; i++) {
      int u = e[i].u, v = e[i].v;
      e[i].u = tid[u], e[i].v = tid[v];
      if (u != v) e[i].w -= val[v];
    }
    n = tot;
  }
  return ans;
}

void solve() {
  m = 0;
  for (int i = 1; i <= n; i++) {
    scanf("%d", a + i);
    mp[i][0] = mp[i - 1][a[i - 1]] + 1;
    for (int j = 1; j <= a[i]; j++) {
      mp[i][j] = mp[i][j - 1] + 1;
      e[++m] = edges(mp[i][j], mp[i][j - 1], 0);
    }
  }
  for (int i = 1; i <= k; i++) {
    int p1, p2, l1, l2, w;
    scanf("%d %d %d %d %d", &p1, &l1, &p2, &l2, &w);
    for (int j = l1; j <= a[p1]; j++) {
      e[++m] = edges(mp[p1][j], mp[p2][l2], w);
    }
  }
  for (int i = 1; i <= n; i++) {
    e[++m] = edges(mp[n][a[n]] + 1, mp[i][0], 0);
  }
  n = mp[n][a[n]] + 1;
  printf("%d\n", edmonds());
}

int main() {
  while (scanf("%d %d", &n, &k) == 2 && n && k) {
    solve();
  }
  return 0;
}

转载于:https://www.cnblogs.com/Juanzhang/p/10389038.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值