Kaka's Matrix Travels

 

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

 

这个题要求的是最大费用最大流,需要拆点,详见注释

#include <iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#define oo 1<<28
#include<queue>
#include<algorithm>
using namespace std;
int pre[210000];
int dis[210000];
int vist[210000];
int head[210000];
int map[10000][10000];
struct node
{
    int u;
    int v;//与u点相连的点
    int f;//容量
    int w;//权值
    int next;//下一条边
} edge[1100000];
int cnt=0;
int s,t;
void add(int u,int v,int f,int w)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].f=f;
    edge[cnt].w=w;
    edge[cnt].next=head[u];
    head[u]=cnt++;

    edge[cnt].u=v;
    edge[cnt].v=u;
    edge[cnt].f=0;
    edge[cnt].w=-w;//反向的权值为正向的相反数
    edge[cnt].next=head[v];
    head[v]=cnt++;
}
void init()
{
    memset(pre,-1,sizeof(pre));
    memset(vist,0,sizeof(vist));
    memset(dis,-1,sizeof(dis));//要求最大值
}
int spfa()
{
    int i;
    init();
    queue<int>q;
    dis[s]=0;
    vist[s]=1;
    q.push(s);
    while(!q.empty())//将相邻点进行松弛,直到队列为空
    {
        int u=q.front();//取出队头
        q.pop();
        i=head[u];
        vist[u]=0;
        while(i!=-1)
        {
            int w=edge[i].w;
            int v=edge[i].v;
            if(edge[i].f>0&&dis[v]<dis[u]+w)//判断是否可以更新
            {
                dis[v]=dis[u]+w;//改进s到v点的值
                pre[v]=i;//记录此点的前驱
                if(!vist[v])//由于距离变小了,如果v点松弛成功且v点不在队列里,因为v点有可能还能改进别的点
                {
                    vist[v]=1;
                    q.push(v);
                }
            }
            i=edge[i].next;
        }
    }
    if(pre[t]==-1)//如果不在最短路中代表寻找失败
        return 0;
    return 1;
}
int Cost()
{
    int ans=0;
    while(spfa())//如果增广路寻找成功
    {
        int maxx=oo;
        int p=pre[t];//初始化P指针
        while(p!=-1)
        {
            maxx=min(edge[p].f,maxx);//求出此增广路上边的最小值
            p=pre[edge[p].u];
        }
        p=pre[t];
        while(p!=-1)
        {
            edge[p].f-=maxx;//正向减去
            edge[p^1].f+=maxx;//反向增加
            ans+=maxx*edge[p].w;//因为以单位计费,所以应当乘上流量
            p=pre[edge[p].u];
        }
    }
    return ans;
}
int main()
{
    //嘤嘤嘤。。老是没输出,最后才发现没对head初始化。。真难过,在POJ上还交错了
    //这个题我看的题解,要拆点,就是将一个点拆成u,v,自身到自身建边,还有自身到右和下建边
    //先由源点到1点,n*n*2到汇点建一条容量为k,权值为0的边,在由自身((i-1)*n+j)*2-1到((i-1)*n+j)*2建两条边
    //一条为容量为1,权值为map[i][j],一条为容量为oo,权值为0的边,最后将自身的v点和右和下的u相连,建一条容量为oo,权值为0的边
    //注意i,j在边的时候
    int n,m,d,k;
    int i,j,l;
    while(~scanf("%d%d",&n,&k))
    {
         memset(head,-1,sizeof(head));
         int u,v,w;
         for(i=1;i<=n;i++)
         {
             for(j=1;j<=n;j++)
             {
                 scanf("%d",&map[i][j]);
             }
         }
         s=0,t=n*n*2+1;
         add(s,1,k,0);//源点与1点相连
         add(n*n*2,t,k,0);//n*n*2与汇点相连
         for(i=1;i<=n;i++)
         {
             for(j=1;j<=n;j++)
             {
                 int a=((i-1)*n+j)*2-1;
                 int b=((i-1)*n+j)*2;
                 //printf("%d %d\n",a,b);
                 add(a,b,1,map[i][j]);//自身出点,入点相连
                 add(a,b,oo,0);
                 if(i!=n&&j!=n)
                 {
                     add(b,b+1,oo,0);//向右
                     add(b,b+2*n-1,oo,0);//向下
                     //printf("%d %d %d\n",b,b+1,b+2*n-1);
                 }
                 else if(i!=n&&j==n)
                 {
                     add(b,b+2*n-1,oo,0);
                 }
                 else if(i==n&&j!=n)
                 {
                     add(b,b+1,oo,0);
                 }
             }
         }
         printf("%d\n",Cost());
    }
    return 0;
}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值