hnust_Derker的博客

哪个描述有意义好呢?

Gym101608K Running Threads(有界最小费用流)

题源
题意:
      k个程序分配了若干个线程任务,每个程序完成一定的线程时会打印出任务进度,任务不会超过一次被执行而且属于同一个程序的任务进度一定是严格递增的,现在k个程序打印出n个任务进度,问最少/最多还有多少线程任务没完成。

思路:
      可以用费用流去做,因为每个进度最多在一个程序里面显示一次而且在同一个程序里面是严格递增的,那么可以这样建图:

  1. 源点向每个进度连边,容量为1, 费用为0,代表该进度最多打印一次。
  2. 每个程序向汇点连边,容量为1,费用为0, 代表一个程序只能有一种严格递增的任务进度。
  3. 每个进度和它后面的大于自己的进度连边,容量为1,费用为0,代表该进度可以在后面那个进度之前打印。
  4. 每个任务进度和分配任务大于该打印进度的程序连边,容量为1,费用为当前进度值,代表该进度可以被该程序打印,而且这里就是终点。
  5. 每个进度要拆成入点和出点,入点和出点连一条边,容量为1,费用为负无穷,因为每个进度一定要被打印一次,该边一定要经过而且流量为1
          如果求剩余最大就求一下最小费用流,求最小值就在4中的步骤将费用置为负值,求一下费用流,最后加上之前置为负无穷的边,因为不能确定流量大小,可以枚举,只有满足必流边满流就是一种合法答案,最后取下所有的最小值即可。

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 3e2 + 10;
const ll INF = 1e12 + 10;
using namespace std;

struct P {
    int from, to;
    ll cap, flow, cost;
    P() {}
    P(int f, int t, ll ca, ll fl, ll c) {
        from = f;   to = t;
        flow = fl;  cost = c;
        cap = ca;
    }
};
vector<P> edge;
vector<int> G[maxn];
int kase = 1, s, t, n, m, T;
ll inq[maxn], d[maxn];
ll p[maxn], a[maxn];
ll flow, cost;

void add(int f, int t, ll c, ll co) {
    edge.push_back(P(f, t, c, 0, co));
    edge.push_back(P(t, f, 0, 0, -co));
    int sz = edge.size();
    G[f].push_back(sz - 2);
    G[t].push_back(sz - 1);
}

bool bellmanford(int s, int t, ll &flow, ll &cost) {
    fill(d, d + maxn, INF);
    memset(inq, 0, sizeof(inq));
    d[s] = 0; inq[s] = 1;
    p[s] = 0; a[s] = INF;
    queue<int> q; q.push(s);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        inq[u] = 0;
        for(int i = 0; i < G[u].size(); i++) {
            P &e = edge[G[u][i]];
            if(e.cap <= e.flow || d[e.to] <= d[u] + e.cost) continue;
            d[e.to] = d[u] + e.cost;
            p[e.to] = G[u][i];
            a[e.to] = min(a[u], e.cap - e.flow);
            if(inq[e.to]) continue;
            q.push(e.to); inq[e.to] = 1;
        }
    }
    if(d[t] == INF) return false;
    flow += a[t]; cost += d[t] * a[t];
    for(int u = t; u != s; u = edge[p[u]].from) {
        edge[p[u]].flow += a[t];
        edge[p[u] ^ 1].flow -= a[t];
    }
    return true;
}

ll mincost(int s, int t) {
    flow = 0; cost = 0;
    while(bellmanford(s, t, flow, cost));
    return cost;
}

ll A[maxn], B[maxn], ans;

bool build_graph(int flag, int mid) {
    edge.clear();
    for(int i = 0; i < maxn; i++) G[i].clear();
    s = 0; t = n + 2 * m + 1;
    for(int i = 1; i <= m; i++) {
        for(int j = i + 1; j <= m; j++) {
            if(B[i] >= B[j]) continue;
            add(n + 2 * i, n + 2 * j - 1, 1, 0);
        }
    }
    for(int i = 1; i <= m; i++) { add(n + 2 * i - 1, n + 2 * i, 1, -INF); add(s, n + 2 * i - 1, 1, 0); }
    for(int i = 1; i <= n; i++) add(i, t, 1, 0);
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            int from = n + 2 * j, to = i;
            if(A[i] >= B[j]) add(from, to, 1, B[j] * flag);
        }
    }
    s = t + 1; add(s, 0, mid, 0);
    ans = mincost(s, t) + INF * m;
    for(int i = 0; i < edge.size(); i++) if(edge[i].cost == -INF && edge[i].flow != edge[i].cap) return false; ///必流边没流
    return true;
}

int main() {
    freopen("threads.in", "r", stdin);
    scanf("%d", &T);
    while(T--) {
        scanf("%d %d", &m, &n);
        ll sum = 0, ans1 = INF, ans2 = INF;
        for(int i = 1; i <= n; i++) { scanf("%lld", &A[i]); sum += A[i]; }
        for(int i = 1; i <= m; i++) scanf("%lld", &B[i]);
        for(int i = 1; i <= n; i++) {
            if(build_graph(1, i)) ans1 = min(ans1, ans);
            if(build_graph(-1, i)) ans2 = min(ans2, ans);
        }
        cout << sum + ans2 << " " << sum - ans1 << endl;
    }
    return 0;
}
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hnust_Derker/article/details/79979871
个人分类: 图论
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

Gym101608K Running Threads(有界最小费用流)

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭