【最小費用流】運輸問題

问题描述: 
W 公司有 m个仓库和 n 个零售商店。第 i 个仓库有 ai 个单位的货物;第 j 个零售商店需要 bj 个单位的货物。货物供需平衡,即∑ai = ∑bj。从第 i 个仓库运送每单位货物到第 j 个零售商店的费用为 cij 。试设计一个将仓库中所有货物运送到零售商店的运输方案,使总运输费用最少。 

编程任务: 
对于给定的 m 个仓库和 n 个零售商店间运送货物的费用,计算最优运输方案和最差运输方案。 

输入数据:
由文件trans.in提供输入数据。文件的第1行有 2 个正整数m和 n,分别表示仓库数和零售商店数。接下来的一行中有m个正整数ai ,1≤i≤m,表示第 i个仓库有ai个单位的货物。再接下来的一行中有n个正整数 bj ,1≤j≤n,表示第j个零售商店需要 bj 个单位的货物。接下来的 m行,每行有n个整数,表示从第i个仓库运送每单位货物到第j个零售商店的费用 cij 。 
输出结果: 
程序运行结束时,将计算出的最少运输费用和最多运输费用输出到文件trans.out中。 

样例输入:
2 3 
220 280 
170 120 210 
77 39 105 
150 186 122

样例输出:
48500
69140

這是一道最小費用流。方法類似於Going Home,也可用KM算法解決。

Accode:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <bitset>
#include <queue>

using std::queue;
using std::bitset;

const char fi[] = "trans.in";
const char fo[] = "trans.out";
const int maxN = 10010;
const int MAX = 0x3f3f3f3f;
const int MIN = ~MAX;

struct Edge
{
    int u, v, f_min, f_max, d;
    Edge *next, *back;
};
Edge *edge[maxN];
Edge *pre[maxN];
int dist[maxN];
queue <int> q;
bitset <maxN> marked;
int n, m, S, T, Min, Max;

void init_file()
{
	freopen(fi, "r", stdin);
	freopen(fo, "w", stdout);
}

inline void insert(int u, int v, int f, int d)
{
    Edge *p = new Edge;
    p -> u = u;
    p -> v = v;
    p -> f_min = p -> f_max = f;
    p -> d = d;
    p -> next = edge[u];
    edge[u] = p;

    p = new Edge;
    p -> u = v;
    p -> v = u;
    p -> f_min = p -> f_max = 0;
    p -> d = -d;
    p -> next = edge[v];
    edge[v] = p;

    edge[u] -> back = edge[v];
    edge[v] -> back = edge[u];
}

void readdata()
{
    scanf("%d%d", &n, &m);
    S = n + m + 1;
    T = n + m + 2;
    int c;
    for (int i = 1; i < n + 1; ++i)
    {
        scanf("%d", &c);
        insert(S, i, c, 0);
    }
    for (int j = 1; j < m + 1; ++j)
    {
        scanf("%d", &c);
        insert(j + n, T, c, 0);
    }
    for (int i = 1; i < n + 1; ++i)
        for (int j = 1; j < m + 1; ++j)
        {
            scanf("%d", &c);
            insert(i, j + n, MAX, c);
        }
}

bool Spfa_min()
{
    while (!q.empty()) q.pop();
    memset(dist, 0x3f, sizeof(dist));
    memset(pre, 0, sizeof(pre));
    q.push(S);
    dist[S] = 0;
    marked.set(S);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        marked.reset(u);
        for (Edge *p = edge[u]; p; p = p -> next)
            if (p -> f_min > 0 && dist[u]
                + p -> d < dist[p -> v])
            {
                dist[p -> v] = dist[u] + p -> d;
                pre[p -> v] = p;
                if (!marked.test(p -> v))
                {
                    q.push(p -> v);
                    marked.set(p -> v);
                }
            }
    }
    return pre[T];
}

bool Spfa_max()
{
    while (!q.empty()) q.pop();
    memset(dist, 0xc0, sizeof(dist));
    memset(pre, 0, sizeof(pre));
    q.push(S);
    dist[S] = 0;
    marked.set(S);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        marked.reset(u);
        for (Edge *p = edge[u]; p; p = p -> next)
            if (p -> f_max > 0 && dist[u]
                + p -> d > dist[p -> v])
            {
                dist[p -> v] = dist[u] + p -> d;
                pre[p -> v] = p;
                if (!marked.test(p -> v))
                {
                    q.push(p -> v);
                    marked.set(p -> v);
                }
            }
    }
    return pre[T];
}

void work()
{
    while (Spfa_min())
    {
        int max_flow = MAX;
        for (Edge *p = pre[T]; p; p = pre[p -> u])
            max_flow = std::min(max_flow, p -> f_min);
        for (Edge *p = pre[T]; p; p = pre[p -> u])
        {
            p -> f_min -= max_flow;
            p -> back -> f_min += max_flow;
        }
        Min += max_flow * dist[T];
    }
    while (Spfa_max())
    {
        int max_flow = MAX;
        for (Edge *p = pre[T]; p; p = pre[p -> u])
            max_flow = std::min(max_flow, p -> f_max);
        for (Edge *p = pre[T]; p; p = pre[p -> u])
        {
            p -> f_max -= max_flow;
            p -> back -> f_max += max_flow;
        }
        Max += max_flow * dist[T];
    }
    printf("%d\n%d", Min, Max);
}

int main()
{
	init_file();
	readdata();
	work();
	exit(0);
}

第二次做:

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>

const int maxN = 1010, SIZE = 0xfff;
const int MAX = 0x3f3f3f3f, MIN = ~MAX;
struct Edge
{int u, v, f_min, f_max, d; Edge *next, *back;};
Edge *edge[maxN], *pre[maxN];
bool marked[maxN];
int dist[maxN], q[SIZE + 1], n, m, S, T, f, r;

inline void Ins(int u, int v, int f, int d)
{
    Edge *p = new Edge;
    p -> u = u; p -> v = v; p -> d = d;
    p -> f_min = p -> f_max = f;
    p -> next = edge[u]; edge[u] = p;
    p = new Edge;
    p -> u = v; p -> v = u; p -> d = -d;
    p -> f_min = p -> f_max = 0;
    p -> next = edge[v]; edge[v] = p;
    edge[u] -> back = edge[v];
    edge[v] -> back = edge[u];
    return;
}

inline int min(int a, int b) {return a < b ? a : b;}
inline int max(int a, int b) {return a > b ? a : b;}

inline bool Spfa(int (*opt)(int, int))
{
    memset(dist, opt(0, 1) ? (~0x3f) : 0x3f, sizeof dist);
    memset(pre, 0, sizeof pre);
    memset(marked, 0, sizeof marked);
    dist[S] = 0; f = r = 0;
    marked[q[r++] = S] = 1; r &= SIZE;
    while (f - r)
    {
        int u = q[f++]; f &= SIZE; marked[u] = 0;
        for (Edge *p = edge[u]; p; p = p -> next)
        if ((opt(0, 1) ? p -> f_max : p -> f_min) > 0)
        {
            int v = p -> v, tmp = opt(dist[v], dist[u] + p -> d);
            if (tmp - dist[v])
            {
                dist[v] = tmp; pre[v] = p;
                if (!marked[v]) {marked[q[r++] = v] = 1; r &= SIZE;}
            }
        }
    }
    return (bool)pre[T];
}

inline int getint()
{
    int res = 0; char tmp;
    while (!isdigit(tmp = getchar()));
    do res = (res << 3) + (res << 1) + tmp - '0';
    while (isdigit(tmp = getchar()));
    return res;
}

int main()
{
    freopen("trans.in", "r", stdin);
    freopen("trans.out", "w", stdout);
    n = getint(); m = getint();
    S = n + m + 1; T = n + m + 2;
    for (int i = 1; i < n + 1; ++i)
        Ins(S, i, getint(), 0);
    for (int j = 1; j < m + 1; ++j)
        Ins(j + n, T, getint(), 0);
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
        Ins(i, j + n, MAX, getint());
    int Min = 0, Max = 0;
    while (Spfa(min))
    {
        int Max_flow = MAX;
        for (Edge *p = pre[T]; p; p = pre[p -> u])
            Max_flow = min(Max_flow, p -> f_min);
        for (Edge *p = pre[T]; p; p = pre[p -> u])
        {
            p -> f_min -= Max_flow;
            p -> back -> f_min += Max_flow;
        }
        Min += Max_flow * dist[T];
    }
    while (Spfa(max))
    {
        int Max_flow = MAX;
        for (Edge *p = pre[T]; p; p = pre[p -> u])
            Max_flow = min(Max_flow, p -> f_max);
        for (Edge *p = pre[T]; p; p = pre[p -> u])
        {
            p -> f_max -= Max_flow;
            p -> back -> f_max += Max_flow;
        }
        Max += Max_flow * dist[T];
    }
    printf("%d\n%d\n", Min, Max);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值