【最小费用最大流】运输问题


在最大流的基础上,找到一个费用最小的。

都还不知道这个算法有没有名字。就是SPFA套一个求最小割。

借用OJ的一个比喻。就是从A到B,有三种路线选择,汽车,飞机,走路,假设走的路都是相同的,这时走路费用是最少的。

因此每次找一条最短路,对他求最小割(也就是最大流),然后将ans加上最大流x最短路。

(可以这样来理解。就是以本题为模型,最短路求的是单价之和,最小割求的是最大的数量)

求最大费用最大流,思路相同,但是要改两个地方,赋初值为负无穷,spfa中比较要反号。


要注意的地方:

1、求完最小费用最大流,不能用一开始的边的信息求最大费用最大流,因为残量被修改了。

我的解决方案是建两条相同的边。

2、求最大费用最大流的时侯除S以外的点要赋初值为负无穷。不能赋为零,因为这样一开始就无法对其他点松弛了。

3、找增广轨的时候一定要判断残量是否大于零,同样适用于这里的找最短路。


话说这道题也可以用KM来做。

题目没有给出数据范围,一开始就随便取了一个,没想到因为空间的缘故,导致超时了。把数据规模改小了之后就过了。我想可能是因为构造函数调用次数太多了吧。

#include <cstdio>
#include <queue>
#include <cstring>

const long maxn=10000;
long m;
long n;
long S;
long T;
bool vis[maxn];
long dist[maxn];
struct node
{
    long i;
    node* next;
};
struct bm
{
    long u;
    long v;
    long cap;
    long val;
    bm* next;
    bm* b;
};
bm* head[maxn];
bm* head2[maxn];
bm* pre[maxn];
long size;
std::queue<long> que;
const long inf = 0x7fff0000;

bool spfa()
{
    while (!que.empty())que.pop();
    memset(vis,0,sizeof(vis));
    memset(pre,0,sizeof(pre));
    memset(dist,0x70,sizeof(dist));
    dist[S] = 0;
    que.push(S);
    vis[S] = true;
    while (!que.empty())
    {
        long u = que.front();
        que.pop();
        vis[u] = false;
        for (bm* vv=head[u]; vv; vv=vv->next)
        {
            long v = vv->v;
            long cap = vv->cap;
            long val = vv->val;
            if (cap>0&&dist[u]+val<dist[v])
            {
                dist[v] = dist[u]+val;
                pre[v] = vv;
                if (!vis[v])
                {
                    vis[v] = true;
                    que.push(v);
                }
            }
        }
    }
    if (pre[T])return true;
    return false;
}

bool spfa2()
{
    while (!que.empty())que.pop();
    memset(vis,0,sizeof(vis));
    memset(pre,0,sizeof(pre));
    memset(dist,0xfe,sizeof(dist));
    dist[S] = 0;
    que.push(S);
    vis[S] = true;
    while (!que.empty())
    {
        long u = que.front();
        que.pop();
        vis[u] = false;
        for (bm* vv=head2[u]; vv; vv=vv->next)
        {
            long v = vv->v;
            long cap = vv->cap;
            long val = vv->val;
            if (cap>0&&dist[u]+val>dist[v])
            {
                dist[v] = dist[u]+val;
                pre[v] = vv;
                if (!vis[v])
                {
                    vis[v] = true;
                    que.push(v);
                }
            }
        }
    }
    if (pre[T])return true;
    return false;
}

void fee_maxflow()
{
    long ans = 0;
    long ans2 = 0;
    long min;
    while (1)
    {
        if (!spfa()) break;
        //ans += dist[T];
        min = inf;
        bm* p = pre[T];
        while (p)
        {
            if (p->cap<min)
                min = p->cap;
            p = pre[p->u];
        }
        ans += min * dist[T];
        p = pre[T];
        while (p)
        {
            p->cap -= min;
            p->b->cap += min;
            p = pre[p->u];
        }
    }
    while (1)
    {
        if (!spfa2()) break;
        //ans += dist[T];
        min = inf;
        bm* p = pre[T];
        while (p)
        {
            if (p->cap<min)
                min = p->cap;
            p = pre[p->u];
        }
        ans2 += min * dist[T];
        p = pre[T];
        while (p)
        {
            p->cap -= min;
            p->b->cap += min;
            p = pre[p->u];
        }
    }
    printf ("%ld\n",ans);
    printf ("%ld\n",ans2);
}

void insert(long u,long v,long cap,long val)
{
    bm* nn = new bm;
    nn->u = u;
    nn->v = v;
    nn->cap = cap;
    nn->val = val;
    nn->next = head[u];
    head[u] = nn;

    nn = new bm;
    nn->u = v;
    nn->v = u;
    nn->cap = 0;
    nn->val = -val;
    nn->next = head[v];
    head[v] = nn;

    head[u]->b = head[v];
    head[v]->b = head[u];
}
void insert2(long u,long v,long cap,long val)
{
    bm* nn = new bm;
    nn->u = u;
    nn->v = v;
    nn->cap = cap;
    nn->val = val;
    nn->next = head2[u];
    head2[u] = nn;

    nn = new bm;
    nn->u = v;
    nn->v = u;
    nn->cap = 0;
    nn->val = -val;
    nn->next = head2[v];
    head2[v] = nn;

    head2[u]->b = head2[v];
    head2[v]->b = head2[u];
}

int main()
{
    freopen("transport.in", "r", stdin);
    freopen("transport.out", "w", stdout);
    scanf("%ld%ld",&m,&n);
    size = m+n+2;
    S = size;
    T = size - 1;
    for (long i=n+1; i<n+m+1; i++)
    {
        long tmp = 0;
        scanf("%ld",&tmp);
        insert(S,i,tmp,0);
        insert2(S,i,tmp,0);
    }
    for (long i=1; i<n+1; i++)
    {
        long tmp = 0;
        scanf("%ld",&tmp);
        insert(i,T,tmp,0);
        insert2(i,T,tmp,0);
    }
    for (long i=n+1; i<n+m+1; i++)
        for (long j=1; j<n+1; j++)
        {
            long tmp = 0;
            scanf("%ld",&tmp);
            insert(i,j,inf,tmp);
            insert2(i,j,inf,tmp);
        }

    fee_maxflow();

    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值