POJ 3422 Kaka's Matrix Travels【最大费用流】

意:给定N,K,和N*N矩阵表示该点权值,从左上到右下,求走K次所得的权值和最大

最大费用流:把最小费用流的权值改为负数,输出也加个负数就行。

建图:因为权值附在点上,所以拆点是必须的,然后点A到点A'要两条边,一条的费用为权值,容量为1,另一条费用为0,容量为K-1,使得走一次时有权,走其他K-1次时没有(每个点最多K次访问)。建立一个源点连接一号点,容量为K,费用0,汇点同理。然后每个点跟自己的左上两点建边(如果有左上的话),容量为K,费用0。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) )
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);

//模板大法好
const int inf = 0x3f3f3f3f;
const int maxn = 5025;
const int maxm = 30000;
struct Edge {
    int to, next, cap, flow, cost;
} edge[maxm * 2];
int head[maxn], tol;
int pre[maxn], dis[maxn];
bool vis[maxn];
void init() {
    tol = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int cap, int cost) {
    edge[tol].to = v;
    edge[tol].cap = cap;
    edge[tol].cost = cost;
    edge[tol].flow = 0;
    edge[tol].next = head[u];
    head[u] = tol++;
    edge[tol].to = u;
    edge[tol].cap = 0;
    edge[tol].cost = -cost;
    edge[tol].flow = 0;
    edge[tol].next = head[v];
    head[v] = tol++;
}
bool spfa(int s, int t) {
    queue<int>q;
    memset(dis, inf, sizeof(dis));
    memset(vis, false, sizeof(vis));
    memset(pre, -1, sizeof(pre));
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if (edge[i].cap > edge[i].flow &&
                    dis[v] > dis[u] + edge[i].cost ) {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if (!vis[v]) {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    return pre[t] != -1;
}
//返回最大流,cost存最小费用
int minCostMaxflow(int s, int t, int &cost) {
    int flow = 0;
    cost = 0;
    while (spfa(s, t)) {
        int Min = inf;
        for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to]) {
            if (Min > edge[i].cap - edge[i].flow) {
                Min = edge[i].cap - edge[i].flow;
            }
        }
        for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to]) {
            edge[i].flow += Min;
            edge[i ^ 1].flow -= Min;
            cost += edge[i].cost * Min;
        }
        flow += Min;
    }
    return flow;
}
int main() {
    int val;
    int n, k;
//    freopen("1.in","r",stdin);
    while (~scanf("%d%d",&n,&k)) {
        int ss = 0, tt = 2*n*n + 1;     //包含了拆点,所以是2*n*n
        init();
        int cnt=1;
        FOR(i,1,n)
        {
            FOR(j,1,n)
            {
                scanf("%d",&val);
                val=-val;   //最大流取负数
                addEdge(cnt,n*n+cnt,1,val);     //A对A'建边
                addEdge(cnt,n*n+cnt,k-1,0);     //A对A'建边
                if(j!=1)
                    addEdge(cnt-1+n*n,cnt,k,0); //不是第一列就跟做左一列的建边
                if(i!=1)
                    addEdge(cnt-n+n*n,cnt,k,0); //不是第一行就对上一行的建边
                cnt++;
            }
        }

        addEdge(ss,1,k,0);      //源点向1号点建K容量边
        addEdge(n*n*2,tt,k,0);  //汇点向终号点建K容量边

        int cost;
        minCostMaxflow(ss, tt, cost);
        printf("%d\n",-cost);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值