ZOJ 3308 最小费用最大流

本文深入讲解了费用流算法的基本原理及实现细节,并通过一个具体的题目示例介绍了如何使用费用流解决实际问题。文章还分享了作者在调试过程中的经验教训。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

不管是最大流还是费用流,模板一般都不会出什么问题,一般错就错在建图……

这也是一道裸的套费用流模板的题,不得不说,费用流代码量真是大,初始化什么的完成基本就快100行代码了。

费用流的基本思想是多次最短路,所以在MCMF函数里会看到while(spfa(st, ed))这么个东西。


先贴代码后分析:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int maxn = 555;
const int maxm = 55555;
const int inf = 1 << 30;
struct node
{
       int to, next, c, f;
}edge[maxm * 10];

int head[maxn];
int src, des, r, c, n, k, sm;
int dis[maxn], load[maxn], p[maxn];
bool vis[maxn][maxn];
bool flag[maxn];
int a[20][20];
int xx[8] = {1, 2, 2, 1, -1, -2, -2, -1};
int yy[8] = {-2, -1, 1, 2, -2, -1, 1, 2};

bool isok(int x, int y)
{
     return (x >= 0 && x < r && y >= 0 && y < c);
}

void add_edge(int u, int v, int c, int f)
{
     edge[sm].to = v;
     edge[sm].c = c;
     edge[sm].f = f;
     edge[sm].next = head[u];
     head[u] = sm ++;
     edge[sm].to = u;
     edge[sm].c = 0;
     edge[sm].f = -f;
     edge[sm].next = head[v];
     head[v] = sm ++;
}

bool spfa(int st, int ed)
{
     int que[maxn * 10], qout, qin;
     memset(flag, false, sizeof(flag));
     memset(load, -1, sizeof(load));
     memset(p, -1, sizeof(p));
     for(int i = 0; i <= des + 1; i ++)
             dis[i] = inf;
     qin = qout = 0;
     que[qin ++] = st;
     dis[st] = 0;
     flag[st] = true;
     while(qin != qout)
     {
               int e = que[qout ++];
               flag[e]  =false;
               for(int i = head[e]; i != -1; i = edge[i].next)
               {
                       if(edge[i].c)
                       {
                                    int ne = edge[i].to;
                                    if(dis[ne] - dis[e] > edge[i].f)
                                    {
                                               dis[ne] = edge[i].f + dis[e];
                                               p[ne] = e;
                                               load[ne] = i;
                                               if(!flag[ne])
                                               {
                                                            flag[ne] = true;
                                                            que[qin ++] = ne;
                                               }
                                    }
                       }
               }
     }
     if(dis[ed] == inf)
                return false;
     return true;
}

int MCMF(int st, int ed)
{
    int u, mn;
    int ans_f = 0, ans_c = 0;
    while(spfa(st, ed))
    { 
                   u = ed;
                   mn = inf;
                   while(p[u] != -1)
                   {
                              mn = min(edge[load[u]].c, mn);
                              u = p[u];
                   }
                   u = ed;
                   while(p[u] != -1)
                   {
                              edge[load[u]].c -= mn;
                              edge[load[u]^1].c += mn;
                              u = p[u];
                   }
                   ans_c += dis[ed] * mn;
                   ans_f += mn;
    }
    if(ans_f != k)
             return -1;
    return ans_c;
}

int main()
{
    while(scanf("%d%d%d%d", &r, &c, &n, &k) != EOF)
    {
                   int i, j, type;
                   for(i = 0; i < r; i ++)
                         for(j = 0; j < c; j ++)
                               scanf("%d", &a[i][j]);
                   memset(head, -1, sizeof(head));
                   memset(vis, 0, sizeof(vis));
                   sm = 0;
                   src = r * c + 1, des = r * c + 2;
                   add_edge(src, r * c, k, 0);
                   for(int u = 0; u < n; u ++)
                   {
                         scanf("%d%d%d", &type, &i, &j);
                         i --, j --;
                         int tmp = i * c + j;
                         add_edge(r * c, tmp, 1, 0);
                         for(int no = 0; no < 8; no ++)
                         {
                                       int px = xx[no] + i;
                                       int py = yy[no] + j;
                                       if(isok(px, py))
                                       {
                                              int tmp2 = px * c + py;
                                              if(type == 1)
                                                           add_edge(tmp, tmp2, 1, a[i][j] * a[px][py]);
                                              if(type == 2)
                                                           add_edge(tmp, tmp2, 1, a[i][j] + a[px][py]);
                                              if(type == 3)
                                                           add_edge(tmp, tmp2, 1, max(a[i][j], a[px][py]));
                                              vis[px][py] = true;
                                       }
                         }
                   }
                   for(i = 0; i < r * c; i ++)
                         if(vis[i / c][i % c])
                                           add_edge(i, des, 1, 0); 
                   int ans = MCMF(src, des);
                   printf("%d\n", ans);
    }
}


一开始一直没调通,后来发现是这个地方出了问题。

for(i = 0; i < r * c; i ++)
       if(vis[i / c][i % c])
                add_edge(i, des, 1, 0); 


之前一直没调出来的程序里是没有if判断的,结果就一直不对。后来突然想到,如果对每个点都向超级终点连边,而且容量是1,费用是0的话,最后就会发生ans = 0的情况,因为好多不相关的点都被连了进去,他们本来的费用是0。


明天继续网络流 + 数据结构。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值