codevs 1227 方格取数 2(最小费用最大流)

题目描述 Description

给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大

输入描述 Input Description

第一行两个数n,k(1<=n<=50, 0<=k<=10)

接下来n行,每行n个数,分别表示矩阵的每个格子的数

输出描述 Output Description

一个数,为最大和

样例输入 Sample Input

3 1

1 2 3

0 2 1

1 4 2

样例输出 Sample Output

11

数据范围及提示 Data Size & Hint

1<=n<=50, 0<=k<=10


题解:



本题是POJ 3422 卡卡的矩阵之旅 的弱化版,同时也是《图论算法理论、实现及应用》中的例题。原题有多组数据。

本题可以转化为最小费用最大流问题。

构建容量网络的方法如下:将每个位置拆成两个——出点和入点,出点和入点之间连接一条容量为1、费用为矩阵中该位置上的数值;若点p与q能连通,则连接p与q,n*n+p与n*n+q,p与n*n+q,n*n+p与q四条边,容量为无穷大,费用为 0;假设源点与汇点,源点与1、汇点与 2*n*n 之间连接边,容量为K,用为0。容量网络构建完成后,求最小费用最大流即可。


code:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

#define INF 0x3f3f3f3f
#define MIN(a,b) ((a)<(b)?(a):(b))

int mat[55][55];
int n,k;

int head[5005];
int adj[25000];
int next[25000];
int point[25000];
int cost[25000];
int cap[25000];

int pre[5005];
int dis[5005];
bool fl[5005];

int max_flow;
int min_cost;

int edge_cnt;

void add(int u,int v,int cst,int cp)
{
    next[edge_cnt]=head[u];
    head[u]=edge_cnt;
    point[edge_cnt]=v;
    adj[edge_cnt]=u;
    cost[edge_cnt]=cst;
    cap[edge_cnt]=cp;
}

void cost_flow(int src,int tar)
{
    while(1)
    {
        memset(pre,-1,sizeof(pre));
        memset(dis,0x3f,sizeof(dis));
        memset(fl,0,sizeof(fl));

        queue<int>q;
        q.push(src);
        dis[src]=0;

        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            fl[u]=0;

            for(int e=head[u];e!=-1;e=next[e])
            {
                if(cap[e]>0&&dis[u]+cost[e]<dis[point[e]])
                {
                    dis[point[e]]=dis[u]+cost[e];
                    pre[point[e]]=e;
                    if(!fl[point[e]])
                    {
                        fl[point[e]]=1;
                        q.push(point[e]);
                    }
                }
            }
        }

        if(pre[tar]==-1)break;

        int min=INF;

        for(int i=tar;pre[i]!=-1;i=adj[pre[i]])
        {
            min=MIN(min,cap[pre[i]]);
        }
        for(int i=tar;pre[i]!=-1;i=adj[pre[i]])
        {
            cap[pre[i]]-=min;
			//反边,反悔的机会
            if(pre[i]&1)cap[pre[i]+1]+=min;
            else cap[pre[i]-1]+=min;
        }

        max_flow+=min;
        min_cost+=min*dis[tar];
    }
}

int main()
{
    while(~scanf("%d%d",&n,&k))
    {
        max_flow=0;
        min_cost=0;

        memset(head,-1,sizeof(head));

        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                scanf("%d",&mat[i][j]);
            }
        }

        edge_cnt=0;

        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                int p3=(i-1)*n+j;
                int p4=(i-1)*n+j+n*n;

                edge_cnt++;
                add(p3,p4,-mat[i][j],1);//负数,所以求的是最小费用,答案添负号就行了
                edge_cnt++;
                add(p4,p3,mat[i][j],1);

                edge_cnt++;
                add(p3,p4,0,k);
                edge_cnt++;
                add(p4,p3,0,0);

                if(i<n)
                {
                    int p1=(i-1)*n+j;
                    int p2=i*n+j;

                    //edge_cnt++;
                    //add(p1,p2,0,INF);

                    //edge_cnt++;
                    //add(p1,p2+n*n,0,INF);

                    edge_cnt++;
                    add(p1+n*n,p2,0,k);
                    edge_cnt++;
                    add(p2,p1+n*n,0,0);

                    //edge_cnt++;
                    //add(p1+n*n,p2+n*n,0,INF);
                }
                if(j<n)
                {
                    int p1=(i-1)*n+j;
                    int p2=(i-1)*n+j+1;

                    //edge_cnt++;
                    //add(p1,p2,0,INF);

                    //edge_cnt++;
                    //add(p1,p2+n*n,0,INF);

                    edge_cnt++;
                    add(p1+n*n,p2,0,k);
                    edge_cnt++;
                    add(p2,p1+n*n,0,0);

                    //edge_cnt++;
                    //add(p1+n*n,p2+n*n,0,INF);
                }
            }
        }

        edge_cnt++;
        add(0,1,0,k);
        edge_cnt++;
        add(1,0,0,0);

        edge_cnt++;
        add(n*n*2,n*n*2+1,0,k);
        edge_cnt++;
        add(n*n*2+1,n*n*2,0,0);

        cost_flow(0,2*n*n+1);

        printf("%d\n",-min_cost);
    }
    return 0;
}


本题是POJ 3422 卡卡的矩阵之旅 的弱化版,同时也是《图论算法理论、实现及应用》中的例题。原题有多组数据。

本题可以转化为最小费用最大流问题。

构建容量网络的方法如下:将每个位置拆成两个——出点和入点,出点和入点之间连接一条容量为1、费用为矩阵中该位置上的数值;若点p与q能连通,则连接p与q,n*n+p与n*n+q,p与n*n+q,n*n+p与q四条边,容量为无穷大,费用为 0;假设源点与汇点,源点与1、汇点与 2*n*n 之间连接边,容量为K,用为0。容量网络构建完成后,求最小费用最大流即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值