UVa 10594 - Data Flow(无向图的最小费用最大流问题)

题目链接:UVa 10594 - Data Flow

这道题是无向图的最小费用最大流问题,看清楚是无向图的。这么说无向图和有向图的费用流问题有什么区别呢?主要是反向边的问题。首先我们说一下最大流问题中的反向边,我们需要将其cap[u][v]=0表示容量为0,而在费用流问题中添加了费用,所以肯定不能像之前那么简单处理了,那怎么办呢?在有向图中,没有存在的反向边我们用cap[u][v]=0表示容量为0,cost[v][u]=-cost[u][v]表示取反的费用,简单说就是讲这部分费用减除,相当于没有走。 现在可以说一下无向图和有向图的不同了,既然两个方向都是可以走的,那么我们就将原本有的一条边变化出了四条边,两个原有边,两个反向边,原有两个边相互独立,不能将这两个原有边看成互为反向边,否则就出现了环路,spfa就走不通了。

 然后说一下最大费用怎么处理,也可以说成要求的费用怎么处理。    加一个点0,表示为起点,设置cap[0][1]=D(题目给的流量D),cost[0][1]=0。显然到终点的最大流量不可能超过D,那么小于D的话表示不可解,等于D表示有解。

要注意一下算法中使用的亦或操作符,有注释。

 这题目就是一个模版题,我就不用我写的了。。我在这里找到了一个比较规整的模版,加了一些注释,下面贴出来,如果不理解跟着代码调试一下就懂了。


#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int MAX = 110;
const long long fd = 1;
const long long INF = fd << 60;
int n,m;
struct edge
{
    int u;
    int v;
    long long cost;
    int flow;
    int cap;
    int next;
}e[50000];

struct node
{
    int u;
    int v;
    int w;
}E[5050];
long long dis[MAX];
int first[MAX];
int p[MAX];
bool vis[MAX];
int edgenum;
int f,d,cap;
long long c;
void add(int u,int v,long long w,int num)
{
    e[edgenum].u = u;
    e[edgenum].v = v;
    e[edgenum].cap = num;
    e[edgenum].cost = w;
    e[edgenum].flow = 0;
    e[edgenum].next = first[u];
    first[u] = edgenum++;
    e[edgenum].u = v;
    e[edgenum].v = u;
    e[edgenum].cap = 0;
    e[edgenum].cost = -w;
    e[edgenum].flow = 0;
    e[edgenum].next = first[v];
    first[v] = edgenum++;
}
void EK()
{
    queue <int> q;
    c = f = 0;
    while(1)
    {
        for(int i = 0;i <= n; i++)
            dis[i] = (i == 0 ? 0 : INF);
        memset(vis,false,sizeof(vis));
        memset(p,-1,sizeof(p));
        q.push(0);
        while(!q.empty())
        {
            int u = q.front();
            q.pop();
            vis[u] = false;
            for(int k = first[u]; k != -1; k = e[k].next)
            {
                int v = e[k].v;
                if(e[k].cap > e[k].flow && dis[v] > dis[u] + e[k].cost)
                {
                    dis[v] = dis[u] + e[k].cost;
                    p[v] = k;
                    if(!vis[v])
                    {
                        vis[v] = true;
                        q.push(v);
                    }
                }
            }
        }
        if(dis[n] == INF)
            break;
        int a = 999999999;
        for(int u = p[n]; u != -1; u = p[e[u].u])//这里的u是指第u条边,不再是邻接矩阵里面的顶点
            a = min(a,e[u].cap - e[u].flow);
        for(int u = p[n]; u != -1; u = p[e[u].u])//增广,这里的u是指第u条边,不再是邻接矩阵里面的顶点
        {
            e[u].flow += a;
            e[u^1].flow -= a;//注意看这里的亦或符号 比如u为2,亦或后就是3了,恰好第二条边的反向边就是第三条边
        }
        c += dis[n] * a;//注意需要乘以距离
        f += a;
    }
}
int main()
{
    int i,j;
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        edgenum = 0;
        memset(first,-1,sizeof(first));
        for(i = 0;i < m; i++)
        {
            scanf("%d %d %lld",&E[i].u,&E[i].v,&E[i].w);
        }
        scanf("%d %d",&d,&cap);
        add(0,1,0,d);
        for(i = 0; i < m; i++)
        {
            add(E[i].u,E[i].v,E[i].w,cap);
            add(E[i].v,E[i].u,E[i].w,cap);
        }
        EK();
        if(f == d)
            printf("%lld\n",c);
        else
            printf("Impossible.\n");
    }
    return 0;
}


  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值