POJ 3422 Kaka's Matrix Travels 解题报告(最大费用最大流)

    题目大意:矩阵中每次只能向下走或者向右走。走K次问能得到的最大的和是多少。

    解题报告:一开始以为是普通的贪心,每次取上边或者是左边中较大的数,取K次。当然,WA了。Discuss里也有讨论,例如下面这种情况:

    1  1  0

    4  3  3

    5  0  1

    贪心的话,第一次取 1,4,3,3,1,第二次取5。当然,最优解是取1,1,3,3,1,和4,5。

    当然,这种情况实在不知道怎么做,那就搜吧。学习了最小费用最大流。

    所谓最小费用最大流,即每次选择费用最小的增广路径进行增广,直到没有增广路径为止。

    寻找最小路径可以用最短路算法。因为可能有负权边,所以使用Bellman算法或者SPFA。

    最小费用最大流与最大费用最大流实际上是一样的。本题求的是最大费用最大流。

    注意建图。要拆点。

    代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int n,ans;
const int maxV=5010;
const int maxE=20010;
int first[maxV],vv[maxE],cap[maxE],cost[maxE],nxt[maxE];
int dis[maxV],pre[maxV],que[maxV];
bool vis[maxV];

bool spfa()
{
    memset(dis,-1,sizeof(dis));
    memset(vis,0,sizeof(vis));

    int top=1,bot=0;
    que[0]=0;
    vis[0]=true;
    dis[0]=0;

    while(top!=bot)
    {
        int u=que[bot++];
        if(bot==maxV) bot=0;
        vis[u]=false;

        for(int e=first[u];e;e=nxt[e])
            if(cap[e] && dis[vv[e]]<dis[u]+cost[e])
        {
            int v=vv[e];
            dis[v]=dis[u]+cost[e];
            pre[v]=e;
            if(!vis[v])
            {
                vis[v]=true;
                que[top++]=v;
                if(top==maxV) top=0;
            }
        }
    }

    return dis[n]>0;
}

void end()
{
    for(int u=n,e;u;u=vv[e^1])
    {
        e=pre[u];
        cap[e]-=1;
        cap[e^1]+=1;
        ans+=cost[e];
    }
}

int e=2;
void addEdge(int u,int v,int ca,int co)
{
    nxt[e]=first[u],vv[e]=v,cap[e]=ca,cost[e]=co,first[u]=e++;
    nxt[e]=first[v],vv[e]=u,cap[e]=0,cost[e]=-co,first[v]=e++;
}

int main()
{
    int N,K;
    int mp[52][52];

    scanf("%d%d",&N,&K);
    for(int i=1;i<=N;i++)
        for(int j=1;j<=N;j++)
            scanf("%d",&mp[i][j]);
    for(int i=1;i<=N;i++)
        for(int j=1;j<=N;j++)
    {
        int a=(i-1)*N+j;
        int b=a+N*N;
        addEdge(a,b,1,mp[i][j]);
        addEdge(a,b,K,0);
        if(i<N) addEdge(b,a+N,K,0);
        if(j<N) addEdge(b,a+1,K,0);
    }

    n=2*N*N+1;
    addEdge(0,1,K,0);
    addEdge(n-1,n,K,0);

    ans=0;
    while(spfa()) end();
    printf("%d\n",ans);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值