poj 3422 Kaka's Matrix Travels 最小费用最大流

我的第一道费用流,难得一次性ac啊,哈哈!不过 debug 了很久。。。建图要小心,要特别小心!!!!!这道题和大一暑假集训做的那个采蘑菇的 gondar 很像,记得哥当时用了一个犀利的一逼的dp搞定了。。。其实一点都不犀利。。。然后 racebug 问我如果有 10 个人怎么办,我想尼玛 10 个人就开个 11 维数组呗。。- -!然后 racebug 说那就果断超时了嘛。。。又说。。那果断就是最大流了!(其实是费用流啊!我说我学完最大流咋还想不出建图。。)。。哥当时傻了。。网络流是啥东西,比 dp 还要牛逼?……*&%%&……%……&% 现在哥终于做出来了。!!。。。

最小费最大流有两种算法,

一种是消圈法,这种方法很好证明,只要证明无负环圈的充要条件是最大流取得最小费~

还有一种是每次用 spfa 搜索最小费用路,然后在这条路上增广,其实就是保证每次增广之后在当前的流量下都是最小费用,所以增广到不能增广的时候就能获得最小费下的最大流了,增广用的是EdmondsKarp算法,因为 spfa的复杂度比 bfs 要高一点,所以总复杂度要比最大流的EdmondsKarp 高一点,也不会高很多,大概就是O(L*V*e2),L是 spfa 里复杂度的常数,一般小于2。

不过这种算法哥还没证明,只会用。。艹。。。。


还有一种很犀利的费用流的算法就是 zkw 算法, sap 多路增广加 km 算法(不过sap不能加当前弧优化,否则就等着wa!),也可以 dinic 加 km,其中 km 算法就是最佳匹配的那个km(最小费最大流也能搞最佳匹配,神器!),zkw哥还不会。。。改天再研究研究。。nnd~

#include<iostream>
#include<vector>
#include<cstring>
#include<queue>
#include<cstdio>
#include<string>
#include<iomanip>
#include<cassert>
using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 5011;
const int maxc = 55;
const int end = 5001;
const int add = 2500;
const int dir[2][2]={{0,1},{1,0}};
struct zz
{
    int from;
    int to;
    int c;
    int cost;
    int id;
}zx,tz;
vector<zz>g[maxn];
queue<int>q;
int n,k;
int a[maxc][maxc];
int way[maxn];
bool inq[maxn];
int backid[maxn];

bool yes(int x,int y)
{
    if(1<=x && x<=n && y>=1 && y<=n)
    {
        return true;
    }
    return false;
}

int num(int x,int y)
{
    return (x-1)*n + y;
}

int dx(int temp)
{
    return (temp-1)/n + 1;
}

int dy(int temp)
{
    return (temp-1)%n + 1;
}

void build()
{
    for(int i=0;i<maxn;i++)
    {
        g[i].clear();
    }
    zx.from = 0;
    zx.to = 1;
    zx.cost = 0;
    zx.c = inf;
    zx.id = g[1].size();
    g[0].push_back(zx);
    swap(zx.from,zx.to);
    zx.c = 0;
    zx.id = g[0].size() - 1;
    g[1].push_back(zx);
    int now,to,nowx,nowy,n2;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            now = num(i,j);
            n2 = now + add;
            for(int k=0;k<2;k++)
            {
                nowx = i + dir[k][0];
                nowy = j + dir[k][1];
                if(!yes(nowx,nowy))
                {
                    continue;
                }
                to = num (nowx,nowy);
                zx.from = now;
                zx.to = to;
                zx.cost = 0;
                zx.c = inf;
                zx.id = g[to].size();
                g[now].push_back(zx);
                swap(zx.from,zx.to);
                zx.c = 0;
                zx.id = g[now].size() - 1;
                g[to].push_back(zx);

                zx.from = n2;
                zx.to = to;
                zx.cost = 0;
                zx.c = inf;
                zx.id = g[zx.to].size();
                g[zx.from].push_back(zx);
                swap(zx.from,zx.to);
                zx.c = 0;
                zx.id = g[zx.to].size() - 1;
                g[zx.from].push_back(zx);

            }
            zx.from = now;
            zx.to = n2;
            zx.c = 1;
            zx.cost = a[i][j];
            zx.id = g[n2].size();
            g[now].push_back(zx);
            swap(zx.from,zx.to);
            zx.c = 0;
            zx.cost = -a[i][j];
            zx.id = g[now].size() - 1;
            g[n2].push_back(zx);
        }
    }
    zx.from = n*n ;
    zx.to = end;
    zx.c = inf;
    zx.cost = 0;
    zx.id = g[end].size();
    g[zx.from].push_back(zx);
    swap(zx.from,zx.to);
    zx.id = g[n*n].size() - 1;
    zx.c = 0;
    g[end].push_back(zx);
    zx.from = n*n + add;
    zx.to = end;
    zx.c = inf;
    zx.cost = 0;
    zx.id = g[end].size();
    g[zx.from].push_back(zx);
    swap(zx.from,zx.to);
    zx.c = 0;
    zx.id = g[zx.to].size() - 1;
    g[zx.from].push_back(zx);
    return ;
}

void spfa()
{
    memset(inq,false,sizeof(inq));
    memset(backid,-1,sizeof(backid));
    for(int i=0;i<maxn;i++)
    {
        way[i] = -inf;
    }
    while(!q.empty())
    {
        q.pop();
    }
    q.push(0);
    inq[0] = true;
    way[0] = 0;
    int now,to,temp,cost;
    while(!q.empty())
    {
        now = q.front();
        q.pop();
        for(int i=0;i<g[now].size();i++)
        {

            if(g[now][i].c > 0)
            {
                to = g[now][i].to;
                cost = g[now][i].cost;
                temp = way[now] + cost;
                if( temp > way[to] )
                {
                    way[to] = temp;
                    backid[to] = g[now][i].id;
                    if(!inq[to])
                    {
                        inq[to] = true;
                        q.push(to);
                    }
                }
            }
        }
        inq[now] = false;
    }
    return ;
}

int change()
{
    spfa();
    int temp = end;
    int id;
    int from;
    int fromid;
    while(backid[temp] != -1)
    {
        id = backid[temp];
        from = g[temp][id].to;
        fromid = g[temp][id].id;
        g[temp][id].c += 1;
        g[from][fromid].c -= 1;
        temp = from;
    }
    if(way[end] > 0)
    {
        return way[end];
    }
    else
    {
        return 0;
    }
}

int ek()
{
    build();
    int ans = 0;
    for(int i=1;i<=k;i++)
    {
        ans += change();
    }
    return ans;
}

int main()
{
    while(cin>>n>>k)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                cin>>a[i][j];
            }
        }
        cout<<ek()<<endl;
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值