最大最小费用流模版

1.AcWing382 k方格取数 最大费用流模版

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
#include <math.h>
using namespace std;

#define INF 0x3f3f3f3f
#define num(i, j, k) ((i - 1) * n + j + k * n * n)

int n , k, tot = 0;
const int N = 55;
struct e
{
    int t;
    int c;
    int w;
    int next;
}edge[N*N*N];
int head[N*N*N] , h[N*N*N], dis[N*N*N], pre[N*N*N], pc[N*N*N], st, ed;
int ansflow , anscost, cnt;
void add(int f, int t, int c, int w)
{
    edge[cnt].t = t;
    edge[cnt].c = c;
    edge[cnt].next = head[f];
    edge[cnt].w = w;
    head[f] = cnt ++;

    edge[cnt].t = f;
    edge[cnt].c = 0;
    edge[cnt].next = head[t];
    edge[cnt].w = -w;
    head[t] = cnt ++;
}
struct node{
    int v, val;
    node(int a, int b):v(a), val(b){}
    bool operator < (const node& no) const{
        // return val > no.val; //最小费用
        return val < no.val; //最大费用
    }
};
void dij()
{
    priority_queue < node > q;
    while(true) //从汇点出发,不断寻找花费最短路作为增广路
    {
        for ( int i = 0 ; i <= ed ; i ++)
            dis[i] = -999999999;
        dis[st] = 0;
        pre[st] = -1;
        pc[st] = INF; //用于记录该增广路所能承受的最大流
        q.push(node(st,0));
        while(!q.empty()) //采用堆优化的dij寻找
        {
            node p = q.top();
            q.pop();
            int u = p.v;
            if(p.val < dis[u]) continue;
            for(int i = head[u] ; i != -1 ; i = edge[i].next)
            {
                int v = edge[i].t , c = edge[i].c, w = edge[i].w;
                int ww = w + h[u] - h[v]; //引入势函数作为新权,保证正数,从而让成功,类似于Johnson算法
                if(c > 0 && dis[u] + ww > dis[v])
                {
                    dis[v] = dis[u] + ww; //修改距离
                    q.push(node(v,dis[v]));
                    pre[v] = i; //记录路径
                    pc[v] = min(pc[u],c); //修改最大流
                }
            }
        }
        if(dis[ed] == -999999999) //如果为INF说明不存在增广路,跳出即可
            break;
        for(int i = 1 ; i <= ed ; i ++) h[i] += dis[i]; //更新势函数
        //        for(int i = 1 ; i <= ed ; i ++) {
        //            if(dis[i] >  -999999999)
        //                h[i] += dis[i];
        //        }
        int nowflow = pc[ed];
        ansflow += nowflow;
        anscost += h[ed]*nowflow;
        for(int i = ed ; pre[i] != -1 ; i = edge[pre[i]^1].t) //递归回溯路径,改变流大小
        {
            edge[pre[i]].c -= nowflow;
            edge[pre[i]^1].c += nowflow;
        }
    }
}
int main(void)
{
    memset(head,-1,sizeof head);
    scanf("%d%d", &n, &k);
    st = num(1, 1, 0), ed = num(n, n, 1);
    for (int i = 1; i <= n; i++) {
        for (int j = 1, x; j <= n; j++) {
            scanf("%d", &x);
            add(num(i, j, 0), num(i, j, 1), 1, x);
            add(num(i, j, 0), num(i, j, 1), k - 1, 0);
            if (i + 1 <= n) add(num(i, j, 1), num(i + 1, j, 0), k, 0);
            if (j + 1 <= n) add(num(i, j, 1), num(i, j + 1, 0), k, 0);
        }
    }
    dij();
    cout << anscost << endl;
}

luoguP3381 最小费用模版

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
#include <math.h>
using namespace std;
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define num(i, j, k) ((i - 1) * n + j + k * n * n)

int n , m, tot = -1;
const int N = 55;
struct e
{
    int t;
    int c;
    int w;
    int next;
}edge[N*N*N];
int head[N*N*N] , h[N*N*N], dis[N*N*N], pre[N*N*N], pc[N*N*N], st, ed;
int ansflow , anscost, cnt = 1;

void add(int f, int t, int c, int w)
{
    edge[++ cnt].t = t;
    edge[cnt].c = c;
    edge[cnt].next = head[f];
    edge[cnt].w = w;
    head[f] = cnt ;

    edge[++ cnt].t = f;
    edge[cnt].c = 0;
    edge[cnt].next = head[t];
    edge[cnt].w = -w;
    head[t] = cnt ;
}

void dij()
{
    priority_queue <pair<int,int> > q;
    while(true) //从汇点出发,不断寻找花费最短路作为增广路
    {
        memset(dis,INF,sizeof(dis));
        dis[st] = 0;
        pre[st] = -1;
        pc[st] = INF; //用于记录该增广路所能承受的最大流
        q.push(make_pair(0,st));
        while(!q.empty()) //采用堆优化的dij寻找
        {
            pair<int,int> p = q.top();
            q.pop();
            int u = p.se;
            if(-p.fi > dis[u]) continue;
            for(int i = head[u] ; i != -1; i = edge[i].next)
            {
                int v = edge[i].t , c = edge[i].c, w = edge[i].w;
                int ww = w + h[u] - h[v]; //引入势函数作为新权,保证正数,从而让成功,类似于Johnson算法
                if(c > 0 && dis[u] + ww < dis[v])
                {
                    dis[v] = dis[u] + ww; //修改距离
                    q.push(make_pair(-dis[v],v));
                    pre[v] = i; //记录路径
                    pc[v] = min(pc[u],c); //修改最大流
                }
            }
        }
        if(dis[ed] == INF) //如果为INF说明不存在增广路,跳出即可
            break;
        for(int i = 1 ; i <= n ; i ++) {
            if(dis[i] < INF)
                h[i] += dis[i];
        }
        int nowflow = pc[ed];
        ansflow += nowflow;
        anscost += h[ed]*nowflow;
        for(int i = ed ; pre[i] != -1 ; i = edge[pre[i]^1].t) //递归回溯路径,改变流大小
        {
            edge[pre[i]].c -= nowflow;
            edge[pre[i]^1].c += nowflow;
        }
    }
}
int main(void)
{
    memset (head, -1 , sizeof head);
    scanf("%d %d %d %d", &n , &m ,&st, &ed);
    for(int i = 1 ; i <= m ; i ++)
    {
        int u , v, c, w;
        scanf("%d %d %d %d", &u , &v ,&c, &w);
        add(u,v,c,w);
    }
    dij();
    cout << ansflow << " "<< anscost << endl;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值