Poj 3422 Kaka's Matrix Travels【最大费用最大流+拆点】

Kaka's Matrix Travels

Time Limit: 1000MS

 

Memory Limit: 65536K

Total Submissions: 9416

 

Accepted: 3823

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 moves one rook from the left-upper grid to the right-bottom one, taking care that the rook moves only to the right or down. Kaka adds the number to SUM in each grid the rook visited, and replaces it with zero. It is not difficult to know the maximum SUM Kaka can obtain for his first travel. Now Kaka is wondering what is the maximum SUM he can obtain after his Kth travel. Note the SUM is accumulative during the K travels.

Input

The first line contains two integers N and K (1 ≤ N ≤ 50, 0 ≤ K ≤ 10) described above. The following N lines represents the matrix. You can assume the numbers in the matrix are no more than 1000.

Output

The maximum SUM Kaka can obtain after his Kth travel.

Sample Input

3 2

1 2 3

0 2 1

1 4 2

Sample Output

15

Source

POJ Monthly--2007.10.06, Huang, Jinsong

 

题目大意:


给你一个n*n的矩阵,起点在左上角,终点在右下角,每一次行走都只能向右或者向下,每走到一个格子,就能拿走格子里的数值(但是只能拿一次),问你可以走k次的条件下,能够拿取的最大值和。


思路:


1、首先想到建图跑费用流,那么问题关键就在建图:

①首先拆点,将一个点拆成两个点,对应其间连一条边,流为1,费用为点权值。因为其流设定为1,那么就保证了每个点的点权只能被拿一次(排除a【1】【1】和a【n】【n】,因为还有源点汇点需要加入)。

②然后建立一个源点S,将源点连入点1,流为k,费用为0。再建立一个汇点T,将点n*n连入汇点,流为k,费用为0.

③那么因为我们引用源点和汇点的建立的特性,我们需要在拆点过程中的时候,1---->1的拆点以及n*n---------->n*n的拆点的流需要设定为k。保证能够跑k次增广路。

④最后建立每个点之间的边,点i能够连入其右边的点,以及其下边的点:

对于点(i,j)建立:

(i,j)拆点-------->(i+1,j);

(i,j)拆点-------->(i,j+1);这两条边表示想要拿取(i+1,j)和(i,j+1)这两个点的价值

如果建边到这里停止,那么最多能够从源点跑出来增广路的条数也就是2,所以我们还需要建立:

(i,j)拆点------->(i+1,j)拆点

(i,j)拆点------->(i,j+1)拆点。这两条边表示不详拿取(i+1,j)和(i,j+1)这两个点的价值。



2、建好图之后,跑SPFA(当然是要跑最长路).连续增广,得到一个最大费用ans。


3、根据我们建图的特性,每一次如果增广成功都加了一次a【1】【1】和a【n】【n】,那么ans=ans-(k-1)*(a【1】【1】+a【n】【n】);注意k==0的ans=0;


Ac代码:


#include<stdio.h>
#include<string.h>
#include<queue>
#include<iostream>
using namespace std;
struct node
{
    int from;
    int to;
    int next;
    int w;
    int f;
    int num;
}e[150*150*10];
int a[150][150];
int head[150*150*2];
int pre[150*150*2];
int path[150*150*2];
int dis[150*150*2];
int vis[150*150*2];
int fx[2]={1,0};
int fy[2]={0,1};
int k,n,ss,cont,tt;
void add(int from,int to,int f,int w)
{
    /*
    if(f)
    {
        printf("%d %d %d %d\n",from,to,f,w);
    }*/
    e[cont].to=to;
    e[cont].f=f;
    e[cont].w=w;
    e[cont].num=cont;
    e[cont].next=head[from];
    head[from]=cont++;
}
void getmap()
{
    cont=0;
    memset(head,-1,sizeof(head));
    ss=n*n*2+1;
    tt=ss+1;
    add(ss,1,k,0);
    add(1,ss,0,0);
    add(n*n*2,tt,k,0);
    add(tt,n*n*2,0,0);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==1&&j==1||i==n&&j==n)
            {
                add((i-1)*n+j,(i-1)*n+j+n*n,k,a[i][j]);
                add((i-1)*n+j+n*n,(i-1)*n+j,0,-a[i][j]);
                continue;
            }
            add((i-1)*n+j,(i-1)*n+j+n*n,1,a[i][j]);
            add((i-1)*n+j+n*n,(i-1)*n+j,0,-a[i][j]);
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            for(int kk=0;kk<2;kk++)
            {
                int x=i+fx[kk];
                int y=j+fy[kk];
                if(x>=1&&x<=n&&y>=1&&y<=n)
                {
                    add((i-1)*n+j+n*n,(x-1)*n+y,k,0);
                    add((x-1)*n+y,(i-1)*n+j+n*n,0,0);
                    add((i-1)*n+j+n*n,(x-1)*n+y+n*n,k,0);
                    add((x-1)*n+y+n*n,(i-1)*n+j+n*n,0,0);
                }
            }
        }
    }
}
int SPFA()
{
    memset(vis,0,sizeof(vis));
    for(int i=0;i<=n*n*2+5;i++)dis[i]=-0x3f3f3f3f;
    dis[ss]=0;
    queue<int >s;
    s.push(ss);
    while(!s.empty())
    {
        int u=s.front();
        //printf("%d\n",u);
        s.pop();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            int f=e[i].f;
            //printf("%d ",v);
            if(f&&dis[v]<dis[u]+w)
            {
                dis[v]=dis[u]+w;
                pre[v]=u;
                path[v]=e[i].num;
                if(vis[v]==0)
                {
                    vis[v]=1;
                    s.push(v);
                }
            }
        }
    }
    //printf("yes\n");
    if(dis[tt]!=-0x3f3f3f3f)return 1;
    else return 0;
}
void MCMF()
{
    int ans=0;
    int maxflow=0;
    while(SPFA()==1)
    {
        int minn=0x3f3f3f3f;
        for(int i=tt;i!=ss;i=pre[i])
        {
            minn=min(minn,e[path[i]].f);
        }
        for(int i=tt;i!=ss;i=pre[i])
        {
            e[path[i]].f-=minn;
            e[path[i]^1].f+=minn;
        }
        maxflow+=minn;
        ans+=minn*dis[tt];
    }
    if(k>1)ans-=(k-1)*(a[1][1]+a[n][n]);
    printf("%d\n",ans);
}
int main()
{
    while(~scanf("%d%d",&n,&k))
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                scanf("%d",&a[i][j]);
            }
        }
        getmap();
        MCMF();
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
百度百科创建词条工具V2.7 我们先了解一下百度百科的优势: 1.权威性,广大网民对百度百科信任度特别高,容易受百科中相关信息影响 2.排名好,绝大部分百科词条(即关键词)能排至各大搜索引擎的前三位 3.流量大,一般词条每天浏览量不亚于一个中型企业站每天的总流量 4.转化率高,百科成为网民上网查资料必看的网站,转化为客户的几率较PPC及PM广告大得多。如病人及其家属查找某疾病如何治疗时,必看百科 5.长期有效,百科广告加上之后长期稳定有效,且不产生后续费用 ………   那么,百度百科创建词条工具可以提供哪些帮助呢? 1.创建百科词条,如品牌名、网站名、产品词、人名、公司名… 2.修改百科词条,在原有词条中加入您的内容,如广告内容、名片… 3.删除百科词条,删除不利词条内容或整个词条(不推荐使用) 4.百科内容撰写,根据客户要求编写词条内容 如有其他需求可及时与客服人员沟通,QQ:120962274 百度百科创建词条网站:www.uducn.com   百度百科创建词条常见错误: 1.修改词条原因不明确。例如:修改原因为“编辑词条”“不具体”“更完善”“更具体”等。 2.修改词条原因错误。例如:修改内容是添加图片,填写的修改原因为添加链接。 3.修改词条原因未能明确说明修改的具体区域。例如:修改了词条中的内容并添加了链接,修改原因应写明:添加内容以及链接,必须指出修改或删除的错误内容,并给出具体理由;只修改错别字,必须指明具体的错字;若您修改了表格中的内容,必须明确指出您修改了表格中的哪部分内容。 4.修改词条不可完全删除原词条的内容,可以选择性删除修改原词条内容。 5.编辑者误将角标误添加在段首,或误添加在了完整段落句号之前。   百度百科创建词条时请注意您的词条中不要有违背“百科原则”的内容,否则词条将被编辑删除,并扣除20分,情节严重者,“百科”有权对其做出关闭部分权限、暂停直至删除其帐号等处罚。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值