POJ-2516 Minimum Cost

题目链接:POJ-2516 Minimum Cost

题意

有$n$个商店,$m$个供货商和$k$种物品,首先$n$行$k$列的矩阵第$i$行$j$列的数字表示第$i$个商店对第$j$种物品的需求量,接下来$m$行$k$列的矩阵第$i$行$j$列的数字表示第$i$个供货商对第$j$种物品的储藏量,接下来$k$个$n$行$m$列的矩阵,第$i$个矩阵第$j$行$p$列表示从第$p$个供应商运输1个单位第$i$种物品到第$j$个商店需要的花费。问满足所有商店的物品需求前提下,最小花费是多少,若不能满足需求,输出-1。


思路

不难看出这是一道最小费用最大流的题目,可对每个商店和供应商都拆成$k$个点建图,但这样流网络的规模较大,很可能会超时。注意到$k$个物品互相是独立的,我们可以对每个物品跑最小费用流,这样就不用拆点,由于用Dijkstra改进算法求解最小费用最大流的时间复杂度为$O(FElogV)$,流网络规模缩小$k$倍后,$F,E,V$都会缩小$k$倍,虽然$k$个物品要跑$k$次,但显然总的时间复杂度仍然明显减小。建图如下:

源点向每个供应商连边,容量为供应商对当前物品的储藏量,费用为0;

每个供应商向每个商店连边,容量为无穷大,费用为当前物品从对应的供应商到对应商店需要的运输单价;

每个商店向汇点连边,容量为商店对当前物品的需求量,费用为0;


代码实现 

#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstdlib>
#include <cstring>
#define N 200
using namespace std;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int to, cap, cost, rev;
    Edge(int t, int c, int cc, int r) :to(t), cap(c), cost(cc), rev(r){}
};
int V;
vector<Edge> G[N];
int h[N];
int dist[N];
int prevv[N];
int preve[N];
int ned[60][60], sup[60][60], k_tot[60];

void addedge(int from, int to, int cap, int cost)
{
    G[from].push_back(Edge(to, cap, cost, G[to].size()));
    G[to].push_back(Edge(from, 0, -cost, G[from].size() - 1 ));
}
int min_cost_flow(int s, int t, int f)
{
    int res = 0;
    fill(h, h + V, 0);
    while (f > 0)
    {
        priority_queue<P, vector<P>, greater<P> >q;
        fill(dist, dist + V, INF);
        dist[s] = 0;
        q.push(P(0, s));
        while (!q.empty())
        {
            P p = q.top(); q.pop();
            int v = p.second;
            if (dist[v] < p.first)continue;
            for (int i = 0; i < G[v].size(); i++)
            {
                Edge &e = G[v][i];
                if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to])
                {
                    dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    q.push(P(dist[e.to], e.to));
                }
            }
        }
        if (dist[t] == INF) return -1;
        for (int j = 0; j < V; j++)
            h[j] += dist[j];
        int d = f;
        for (int x = t; x != s; x = prevv[x])
            d = min(d, G[prevv[x]][preve[x]].cap);
        f -= d;
        res += d * h[t];
        for (int x = t; x != s; x = prevv[x])
        {
            Edge &e = G[prevv[x]][preve[x]];
            e.cap -= d;
            G[x][e.rev].cap += d;
        }
    }
    return res;
}

int main()
{
    int n, m, k;
    while (~scanf("%d %d %d", &n, &m, &k) && (n || m || k)) {
        V = n + m + 2;
        memset(k_tot, 0, sizeof(k_tot));
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < k; j++) {
                scanf("%d", &ned[i][j]);
                k_tot[j] += ned[i][j];
            }
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < k; j++) {
                scanf("%d", &sup[i][j]);
            }
        }
        int s = n + m, t = n + m + 1, ans = 0;
        for (int i = 0; i < k; i++) {
            for (int j = 0; j < V; j++) G[j].clear();
            for (int j = 0; j < m; j++) addedge(s, j, sup[j][i], 0);
            for (int j = 0; j < n; j++) addedge(m + j, t, ned[j][i], 0);
            for (int j = 0, cost; j < n; j++) {
                for (int p = 0; p < m; p++) {
                    scanf("%d", &cost);
                    addedge(p, j + m, INF, cost);
                }
            }
            if (ans == -1) continue;
            int tmp = min_cost_flow(s, t, k_tot[i]);
            if (tmp == -1) ans = -1;
            else ans += tmp;
        }
        printf("%d\n", ans);
    }
    return 0;
}
View Code

 

posted @ 2019-08-09 19:10  _kangkang  阅读(193)  评论(0编辑  收藏  举报