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();
}
}