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。容量网络构建完成后,求最小费用最大流即可。

内容概要:本文详细介绍了900W或1Kw,20V-90V 10A双管正激可调电源充电机的研发过程和技术细节。首先阐述了项目背景,强调了充电机在电动汽车和可再生能源领域的重要地位。接着深入探讨了硬件设计方面,包括PCB设计、磁性器件的选择及其对高功率因的影响。随后介绍了软件实现,特别是程序代码中关键的保护功能如过保护的具体实现方法。此外,文中还提到了充电机所具备的各种保护机制,如短路保护、欠压保护、电池反接保护、过保护和过温度保护,确保设备的安全性和可靠性。通讯功能方面,支持RS232隔离通讯,采用自定义协议实现远程监控和控制。最后讨论了散热设计的重要性,以及为满足量产需求所做的准备工作,包括提供详细的PCB图、程序代码、BOM清单、磁性器件和散热片规格书等源文件。 适合人群:从事电力电子产品研发的技术人员,尤其是关注电动汽车充电解决方案的专业人士。 使用场景及目标:适用于需要高效、可靠充电解决方案的企业和个人开发者,旨在帮助他们快速理解和应用双管正激充电机的设计理念和技术要点,从而加速产品开发进程。 其他说明:本文不仅涵盖了理论知识,还包括具体的工程实践案例,对于想要深入了解充电机内部构造和工作原理的人来说是非常有价值的参考资料。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值