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

题目在《挑战程序设计竞赛》的练习题的最小费用流分类里。看到后第一反应是dp,想到了 51Nod 1084 矩阵取数问题 V2 这个题,这个题是个多进程dp,从左上走到右下,走两次。然后就去搜了下这个题的题解:http://www.hankcs.com/program/algorithm/poj-3422-kakas-matrix-travels.html 主要是不怎么会建图,看了下题解的注释,然后画了画,突然想到怎么建图了,然后就是建图,模板,ac。。。。
看到好多题解把这个题分类到 最小费用最大流,感觉分错类了吧,这个是 最小费用流啊,给定固定的流量,求最小花费。 最小费用最大流应该是求最大流里面花费最小的吧。。。。
思路:拆点,建图,每个点拆成两个点,中间连一条边,容量为1,花费为那点的值。看完题最大的疑惑就是k,0<=k<=10,就只能向左向下走两次,k怎么还能到10???看了题解才发现可以重复走,重复走就是把一个点拆成两个点时,中间加两条边,一条边容量为1,花费为那点的值。另一条边容量为k,花费为0,这样就可以重复走了。

#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;

struct edge
{
    int to,cap,cost,rev;
    edge(int _to = 0, int _cap = 0, int _cost = 0, int _rev = 0)
        :to(_to),cap(_cap),cost(_cost),rev(_rev) {}
};
const int INF = 0x3f3f3f3f;
const int MAX_V = 5555;
int V;
vector<edge> G[MAX_V];
int dist[MAX_V];
int prevv[MAX_V],preve[MAX_V];

void add_edge(int from, int to, int cap, int cost)
{
    G[from].push_back(edge(to,cap,cost,G[to].size()));
    G[to].push_back(edge(from,0,-cost,G[from].size()-1));
}

int min_cost_flow(int s, int t, int f)
{
    int res = 0;
    while(f > 0)
    {
        memset(dist,0x3f,sizeof(dist));
        dist[s] = 0;
        bool update = true;
        while(update)
        {
            update = false;
            for(int v = 0; v < V; ++v)
            {
                if(dist[v] == INF) continue;
                for(int i = 0; i < G[v].size(); ++i)
                {
                    edge& e = G[v][i];
                    if(e.cap > 0 && dist[e.to] > dist[v] + e.cost)
                    {
                        dist[e.to] = dist[v] + e.cost;
                        prevv[e.to] = v;
                        preve[e.to] = i;
                        update = true;
                    }
                }
            }
        }
        if(dist[t] == INF)
            return -1;
        int d = f;
        for(int v = t; v != s; v = prevv[v])
            d = min(d,G[prevv[v]][preve[v]].cap);
        f -= d;
        res += d*dist[t];
        for(int v = t; v != s; v = prevv[v])
        {
            edge& e = G[prevv[v]][preve[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return res;
}

const int MAXN = 55;
int GG[MAXN][MAXN];

int main()
{
    int n,k,s,t,cur1,cur2,nexth,nextv;
    scanf("%d %d",&n,&k);
    for(int i = 0; i < n; ++i)
        for(int j = 0; j < n; ++j)
            scanf("%d",&GG[i][j]);
    V = 2*n*n+2;
    for(int i = 0; i <= V; ++i) G[i].clear();
    s = 0,t = V-1;
    add_edge(s,1,k,0);//连接到源点
    for(int i = 0; i < n; ++i)
    {
        for(int j = 0; j < n; ++j)
        {
            cur2 = i*(2*n) + (j+1)*2;
            cur1 = cur2 - 1;
            add_edge(cur1,cur2,1,-GG[i][j]);
            add_edge(cur1,cur2,k,0);

            if(i+1 < n)
            {
                nextv = (i+1)*(2*n) + (j+1)*2 - 1;
                add_edge(cur2,nextv,k,0);
            }
            if(j+1 < n)
            {
                nexth = i*(2*n) + (j+2)*2 - 1;
                add_edge(cur2,nexth,k,0);
            }
        }
    }
    add_edge(t-1,t,k,0);//连接到汇点
    int res = min_cost_flow(s,t,k);
    printf("%d\n",-res);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值