POJ3422 Kaka's Matrix Travels(最大费用最大流 + 拆点)

题目链接:http://poj.org/problem?id=3422

题意有一个n*n的矩阵,格子中的元素是费用,KaKa从左上角开始出发要到达右下角,但是他只能向下走或者向右走,且走过的格子赋值为0,可以走K次,问K次后KaKa能获得的最大费用是多少?

思路:首先解释一下为什么要拆点?

   因为要获得最大费用,所以假设当前步选择先下走,最终得到的结果可能不是最大值,但根据题意却把走过的格子赋为0了,这就影响了最终结果。所以进行拆点,把每个点拆成两个点,入度点和出度点,本点的入度点连接着本点的出度点,费用为本点格子的值的负值,容量为1(因为当前点的入度点只能由(上边点)/(左边点)的出度点连接,所以容量是1),这样使用流网络就保证了最终的结果不受一些不是最优解的中间过程的影响。而当前点的出度点连接下个点的入度点的费用为0,容量为K,因为可以走K次,所以在走过一次后,费用已经赋为0,容量就变成K-1,谁走进当前格子,对整体的费用已经没有影响了。

再建立一个源点和一个汇点,源点与1点(左上角)相连费用0,容量K,汇点与2*n*n+1点(右下角)相连费用0,容量为K

求最大费用最大流,可以再建图的时候把费用改为负值,在求最小费用最大流,也可以再SPFA中改最长路,直接求最大费用最大流

第一种

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
const int maxn = 20000;
const int maxm = 800000;
const int inf = 1e8;
#define MIN INT_MIN
#define MAX 1e6
#define LL long long
#define init(a) memset(a,0,sizeof(a))
#define FOR(i,a,b) for(int i = a;i<b;i++)
#define max(a,b) (a>b)?(a):(b)
#define min(a,b) (a>b)?(b):(a)
using namespace std;
struct node
{
    int u,v,w,cap,next;
} edge[maxm];
int pre[maxn],dis[maxn],head[maxn],cnt;
bool vis[maxn];
int n;
void add(int u,int v,int c,int cap)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].w=c;
    edge[cnt].cap=cap;
    edge[cnt].next=head[u];
    head[u]=cnt++;

    edge[cnt].u=v;
    edge[cnt].v=u;
    edge[cnt].w=-c;
    edge[cnt].cap=0;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}
int spfa(int s,int t)
{
    queue<int>q;
    while(q.empty()==false) q.pop();
    q.push(s);
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    FOR(i,s,t+1)
    dis[i] = inf;
    dis[s]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u] = 0;
        for(int i=head[u]; i!=-1; i=edge[i].next)
        {
            if(edge[i].cap && dis[edge[i].v]>dis[u]+edge[i].w)
            {
                dis[edge[i].v]=dis[u]+edge[i].w;
                pre[edge[i].v] = i;
                if(!vis[edge[i].v])
                {
                    vis[edge[i].v]=1;
                    q.push(edge[i].v);
                }
            }
        }

    }
    if(dis[t]  != inf)
        return 1;
    else
        return 0;
}
int MinCostMaxFlow(int s,int t)
{
    int flow=0,cost=0;
    while(spfa(s,t))
    {
        int df = inf;
        for(int i = pre[t]; i!=-1; i=pre[edge[i].u])
        {
            if(edge[i].cap<df)
                df = edge[i].cap;
        }
        flow += df;
        for(int i=pre[t]; i!=-1; i=pre[edge[i].u])
        {
            edge[i].cap -= df;
            edge[i^1].cap += df;
        }
        //printf("df = %d\n",df);
        cost += dis[t] * df;
    }
    return cost;
}
void initt()
{
    cnt=0;
    memset(head,-1,sizeof(head));
}

int ma;
int main()
{
    int s,t,k;
    while(~scanf("%d%d",&n,&k))
    {
        initt();
        s = 0;
        t=2*n*n+1;
        add(s,1,0,k);
        int num = n*n;
        FOR(i,1,n+1)
        {
            FOR(j,1,n+1)
            {
                scanf("%d",&ma);
                add((i-1)*n+j,(i-1)*n+j+num,-ma,1);
                add((i-1)*n+j,(i-1)*n+j+num,0,k-1);

                if(i<=n-1)//向下建图
                {
                    add((i-1)*n+j+num,i*n+j,0,k);
                }
                if(j<=n-1)//向右建图
                {
                     add((i-1)*n+j+num,(i-1)*n+j+1,0,k);
                }
            }
        }
        add(t-1,t,0,k);
        int ans =  MinCostMaxFlow(s,t);
        cout<<abs(ans)<<endl;
    }
    return 0;
}

求最长路的最大费用最大流

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
const int maxn = 20000;
const int maxm = 800000;
const int inf = 1e8;
const int INF = 0x3f3f3f3f;
#define MIN INT_MIN
#define MAX 1e6
#define LL long long
#define init(a) memset(a,0,sizeof(a))
#define FOR(i,a,b) for(int i = a;i<b;i++)
#define max(a,b) (a>b)?(a):(b)
#define min(a,b) (a>b)?(b):(a)
using namespace std;
struct node
{
    int u,v,w,cap,next;
} edge[maxm];
int pre[maxn],dis[maxn],head[maxn],cnt;
bool vis[maxn];
int n;
void add(int u,int v,int c,int cap)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].w=c;
    edge[cnt].cap=cap;
    edge[cnt].next=head[u];
    head[u]=cnt++;

    edge[cnt].u=v;
    edge[cnt].v=u;
    edge[cnt].w=-c;
    edge[cnt].cap=0;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}
int spfa(int s,int t)
{
    queue<int>q;
    while(q.empty()==false) q.pop();
    q.push(s);
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    FOR(i,s,t+1)
    dis[i] = -1;//求最长路dis数组初始化为-1

    dis[s]=0;
    vis[s] = 1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u] = 0;
        for(int i=head[u]; i!=-1; i=edge[i].next)
        {
            if(edge[i].cap && dis[edge[i].v] < (dis[u]+edge[i].w))//求最长路
            {
                dis[edge[i].v] = dis[u]+edge[i].w;
                pre[edge[i].v] = i;
                if(!vis[edge[i].v])
                {
                    vis[edge[i].v]=1;
                    q.push(edge[i].v);
                }
            }
        }

    }
    if(dis[t] != -1)//------------------忘改了。。
        return 1;
    else
        return 0;
}
int MinCostMaxFlow(int s,int t)
{
    int flow=0,cost=0;
    while(spfa(s,t))
    {
        int df = inf;
        for(int i = pre[t]; i!=-1; i=pre[edge[i].u])
        {
            if(edge[i].cap<df)
                df = edge[i].cap;
        }
        flow += df;
        for(int i=pre[t]; i!=-1; i=pre[edge[i].u])
        {
            edge[i].cap -= df;
            edge[i^1].cap += df;
        }
        //printf("df = %d\n",df);
        cost += dis[t] * df;
    }
    return cost;
}
void initt()
{
    cnt=0;
    memset(head,-1,sizeof(head));
}

int ma;
int main()
{
    int s,t,k;
    while(~scanf("%d%d",&n,&k))
    {
        initt();
        s = 0;
        t=2*n*n+1;
        add(s,1,0,k);
        int num = n*n;
        FOR(i,1,n+1)
        {
            FOR(j,1,n+1)
            {
                scanf("%d",&ma);
                add((i-1)*n+j,(i-1)*n+j+num,ma,1);
                add((i-1)*n+j,(i-1)*n+j+num,0,k-1);//本点与拆点连线,费用0

                if(i<=n-1)//向下建图
                {
                    add((i-1)*n+j+num,i*n+j,0,k);
                }
                if(j<=n-1)//向右建图
                {
                     add((i-1)*n+j+num,(i-1)*n+j+1,0,k);
                }
            }
        }
        add(t-1,t,0,k);

        int ans =  MinCostMaxFlow(s,t);
        printf("%d\n",ans);
    }
    return 0;
}

POJ3422 Kaka's Matrix Travels(最大费用最大流 + 拆点),布布扣,bubuko.com

POJ3422 Kaka's Matrix Travels(最大费用最大流 + 拆点)

时间: 2024-10-17 02:00:24

POJ3422 Kaka's Matrix Travels(最大费用最大流 + 拆点)的相关文章

POj3422 Kaka&#39;s Matrix Travels 最小费用最大流 拆点

题目链接: poj3422 题意: 有一个N X N的矩阵, 矩阵的每一小格都有且仅有一个数字v,kaka每经过一个数字就会把它捡起来并将那个数字加到sum上去. 现在kaka能通过向下或向右走的方式从 (1,1)到(n,n)  K次,问kaka能取到的最大的sum是多少. 解题思路: 题目问题可以抽象为   求多次可走重复路的最长路的最大和问题 首先想到的就应该是最小费用最大流, 而题目要求的最大和值,那么应求就是最大费用最大流, 仅仅只需要将代码中的最小路算法改为最长路算法即可 题目的条件是

poj3422 Kaka&#39;s Matrix Travels(最小费用最大流问题)

1 /* 2 poj3422 Kaka's Matrix Travels 3 不知道 k次 dp做为什么不对??? 4 看了大牛的代码,才知道还可以这样做! 5 开始没有理解将a 和 a‘ 之间建立怎样的两条边,导致程序一直陷入死循环,真心花了好长时间,快崩溃了.无语..... 6 题意:有个方阵,每个格子里都有一个非负数,从左上角走到右下角,每次走一步,只能往右或往下走,经过的数字拿走 7 每次都找可以拿到数字和最大的路径走,走k次,求最大和 8 9 这是 最大费用最大流 问题 每次spfa都

POJ3422 Kaka&#39;s Matrix Travels[费用流]

Kaka's Matrix Travels Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9522   Accepted: 3875 Description On an N × N chessboard with a non-negative number in each grid, Kaka starts his matrix travels with SUM = 0. For each travel, Kaka mo

POJ3422 Kaka&#39;s Matrix Travels 【最大费用最大流】

Kaka's Matrix Travels Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8006   Accepted: 3204 Description On an N × N chessboard with a non-negative number in each grid, Kaka starts his matrix travels with SUM = 0. For each travel, Kaka mo

POJ 3422 Kaka&#39;s Matrix Travels(费用流)

POJ 3422 Kaka's Matrix Travels 题目链接 题意:一个矩阵,从左上角往右下角走k趟,每次走过数字就变成0,并且获得这个数字,要求走完之后,所获得数字之和最大 思路:有点类似区间k覆盖的建图方法,把点拆了,每个点有值的只能选一次,其他都是无值的,利用费用流,入点出点之间连一条容量1,有费用的边,和一条容量k - 1,费用0的边,然后其他就每个点和右边和下边2个点连边,然后跑费用流 代码: #include <cstdio> #include <cstring&g

poj 3422 Kaka&#39;s Matrix Travels (费用流)

Kaka's Matrix Travels Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7743   Accepted: 3111 Description On an N × N chessboard with a non-negative number in each grid, Kaka starts his matrix travels with SUM = 0. For each travel, Kaka mo

poj 3422 Kaka&#39;s Matrix Travels 最小费最大流

输入的时候没有取反,一直ole. 这里也是用到拆点,将一个点拆成p和q,这两个之间连接两条路,一条cap=1和cost=矩阵上的值,另一条为cap=k和cost=0.在将0和2*n *n+1看成源点和汇点. #include<stdio.h> #include<string.h> #include<vector> #include<queue> #include<algorithm> using namespace std; const int

POJ3422:Kaka&#39;s Matrix Travels——题解

http://poj.org/problem?id=3422 题目大意: 从左上角走到右下角,中途取数(数>=0),然后该点的数变为0,求走k的总价值和最大值. —————————————————————————————— 最大值?但是我们只会最小费用流啊…… 但是数是>=0的啊,所以…… 我们拆点,中间连一条容量为1费用为当前值负值的边,再连一条容量为k-1费用0的边. 这样就一定会先走前一条边啦! 然后向下向右连一条容量为k费用0的边. 源点到左上角连一条容量为k-1费用0的边. 右下角到

【poj3422】 Kaka&#39;s Matrix Travels

http://poj.org/problem?id=3422 (题目链接) 题意 N*N的方格,每个格子中有一个数,寻找从(1,1)走到(N,N)的K条路径,使得取到的数的和最大. Solution 同[codevs1277] 方格取数 代码 // poj3422 #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio>