poj 2516 Minimum Cost(最小费用流基础)

题意:
。。。
思路:
这里是求最大流的最小费用流。
k个物品是无关的,可以分开搞,最后把费用加起来。
参考了大神代码

const int Maxn = 50;
const int MaxV = 105;
struct Edge {
    int to, cap, cost;
};

vector<Edge> E;
vector<int> G[MaxV+5];

int offer[Maxn+5][Maxn+5], need[Maxn+5][Maxn+5], carriage[Maxn+5][Maxn+5][Maxn+5], n, m, k;

int inq[MaxV+5], preE[MaxV+5], preV[MaxV+5], mc[MaxV+5], dist[MaxV+5];
int spfa (int s, int t) {
    queue<int> q;
    memset(inq, 0, sizeof(inq));
    fill(mc, mc+t+2, inf);
    fill(dist, dist+t+2, inf);
    dist[s] = 0;
    inq[s] = 1;
    q.push(s);
    while (!q.empty()) {
        int fr = q.front();q.pop();
        inq[fr] = 0;
        int sz = G[fr].size();
        rep(i, 0, sz-1) {
            Edge &e = E[G[fr][i]];
            if (e.cap && dist[fr] + e.cost < dist[e.to]) {
                preV[e.to] = fr;
                preE[e.to] = G[fr][i];
                dist[e.to] = dist[fr] + e.cost;
                mc[e.to] = min (e.cap, mc[fr]);
                if (!inq[e.to]) {
                    q.push(e.to);inq[e.to] = 1;
                }
            }
        }
    }
    //rep(i, 1, t) cout << "d" << i << ": "  << dist[i] << endl;
    //rep(i, 1, t) cout << "pre" << i << ": " << preV[i] << endl;
    if (dist[t] < inf) return dist[t];
    return -1;
}

void add_edge(int from, int to, int cap, int cost) {
    E.push_back( (Edge){to, cap, cost} );
    G[from].push_back(E.size()-1);
    E.push_back( (Edge){from, 0, -cost} );
    G[to].push_back(E.size()-1);
}

// supply places i: i, shopkeeper i: i+m
void build(int s, int t, int x) {
    rep(i, 1, m) add_edge(s, i, offer[i][x], 0);
    rep(i, 1, n) add_edge(i+m, t, need[i][x], 0);
    rep(i, 1, m) rep(j, 1, n) add_edge(i, j+m, need[j][x], carriage[x][j][i]);
}

void change(int s, int t, int f) {
    int now = t;
    while (now != s) {
        int idx = preE[now];
        E[idx].cap -= f;
        E[idx^1].cap += f;
        now = preV[now];
    }
}

int work(int x) {
    int s = 0, t = n+m+1;
    rep(i, s, t) G[i].clear();
    E.clear();
    build(s, t, x);
    int ret = 0;
    while (true) {
        int tmp = spfa(s, t);
        if (tmp < 0) break;
        change(s, t, mc[t]);
        ret += tmp*mc[t];
    }
    return ret;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.in", "r", stdin);
#endif
    SPEED_UP

    while (cin >> n >> m >> k && n+m+k) {
        vector<int> sum_of_need(k+1), sum_of_offer(k+1);
        rep(i, 1, n) rep(j, 1, k) {cin >> need[i][j];sum_of_need[j] += need[i][j];}
        rep(i, 1, m) rep(j, 1, k) {cin >> offer[i][j];sum_of_offer[j] += offer[i][j];}
        rep(i, 1, k) rep(j, 1, n) rep(z, 1, m) cin >> carriage[i][j][z];

        // 判断 impossible
        int ok = 1;
        rep(i, 1, k) if (sum_of_offer[i] < sum_of_need[i]) {
            ok = 0;break;
        }
        if (!ok) {cout << -1 << endl;continue;}

        int sum = 0;
        rep(i, 1, k) {
            sum += work(i);
        }
        cout << sum << endl;
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值