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

大致题意:
给出N和K,表示一个N*N个区域组成的的方阵map,方阵的每个区域都有自己的一个值map[i][j]。现从(0,0)点走到(n,n)点走K次,规定只能向右走或者向下走,每经过一个区域取走区域中的值,map[i][j]变为0,一个区域可以被多次经过。求最多能取走多少值。

大致思路:
    最小费用最大流。说实话一直觉得这道题算法叫最大费用最大流更合适,因为这里求的是取走费用总和的最大值。但是终归还是要用费用流的模版,毕竟只是最小费用流模版改了几个小地方而已。
    建图方案。因为这里并不规定一个区域最多走多少次,但是能获得其上面数字map[i][j]的次数只有一次,所以这里可以将一个点拆作两个点再用两条边连接他们。一条的流量是1费用是map[i][j],代表能得到收入map[i][j]的次数只有一次。另一条流量是inf费用是0,代表这个点可以被0收入地经过无数次。再将其拆出的第二个点与其下方和右方的点连上一条边,流量为inf费用为0。设一个超级原点0连接1,这条边流量是K,费用是0。然后对构造出的图从0到第2*N*N个点使用最小费用最大流模版。
    说到模版,本来想带poj 2135的模版来用,没想到tle了,最后看大牛的博客说在用spfa时使用堆栈会超时,换队列的spfa模版过了。哪位大牛知道这是为何的话麻烦帮忙给我解释解释啊。

详细代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int inf=99999999;
const int nMax=5005;
struct{
    int v, cap, cost, next, re;
}edge[40005];
int n,m,ans;
int k,edgeHead[nMax];
int que[5005],pre[5005],dis[5005];
bool vis[5005];
void addEdge(int u,int v,int ca,int co){
    edge[k].v=v;
    edge[k].cap=ca;
    edge[k].cost=co;
    edge[k].next=edgeHead[u];
    edge[k].re=k + 1;
    edgeHead[u]=k ++;
    edge[k].v=u;
    edge[k].cap=0;
    edge[k].cost=-co;
    edge[k].next=edgeHead[v];
    edge[k].re=k - 1;
    edgeHead[v]=k ++;
}

bool spfa(){
    int i, head = 0, tail = 1;    //  长注释的地方就是从最小费用改到最大费用时需要变动的地方
    for(i = 0; i <= n; i ++){
        dis[i] = -1;
        vis[i] = false;
    }
    dis[0] = 0;
    que[0] = 0;
    vis[0] = true;
    while(head != tail){
        int u = que[head];
        for(i = edgeHead[u]; i != 0; i = edge[i].next){
            int v = edge[i].v;
            if(edge[i].cap && dis[v] <dis[u] + edge[i].cost){
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if(!vis[v]){
                    vis[v] = true;
                    que[tail ++] = v;
                    if(tail == 5005) tail = 0;
                }
            }
        }
        vis[u] = false;
        head++;
        if(head ==5005) head = 0;
    }
    if(dis[n] ==-1) return false;///
    return true;
}

void end(){
    int u, p;
    for(u = n; u != 0; u = edge[edge[p].re].v){
        p = pre[u];
        edge[p].cap -= 1;
        edge[edge[p].re].cap += 1;
        ans += edge[p].cost;
    }
}
int main(){
    int map[60][60];
    int a,b,c,d,i,j,N,K;
    k=1;
    memset(edgeHead,0,sizeof(edgeHead));
    memset(vis,0,sizeof(vis));
    scanf("%d%d",&N,&K);
    for(i=1;i<=N;i++){
        for(j=1;j<=N;j++){
            scanf("%d",&map[i][j]);
        }
    }
    addEdge(0,1,K,0);
    for(i=1;i<=N;i++){
        for(j=1;j<=N;j++){
            a=(i-1)*N+j;
            a=a*2-1;
            b=a+1;
            addEdge(a,b,1,map[i][j]);
            addEdge(a,b,K,0);
            if(i<N)addEdge(b,a+2*N,K,0);
            if(j<N)addEdge(b,b+1,K,0);
        }
    }
    n=2*N*N;
    ans=0;
    while(spfa())end();
    cout<<ans<<endl;
    return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值