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

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

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

输出格式
一个数,为最大和

输入输出样例

输入
3 1
1 2 3
0 2 1
1 4 2
输出
11

相信做到这道题,大多数人应该做过k等于2的情况吧,当k等于2时,我们可以直接dp,令 dp[i][j][k][l] 为第一个人走到(i,j),第二个人走到(k,l)的情况,就可以很容易dp出来。


但是有了网络流,还要dp干嘛?


对于这道题,考虑建图:

不难看出,这道题我们有两个要关注的东西,就是取k次,和最大和。

那么我们把每个点拆成两个点,这两个点分为入点和出点。

入点到出点有两条边,第一条的费用为当前格子的值,流量为1,第二条的费用为0,流量为k-1;然后出点指向相邻的两个格子的入点。

这样我们求点(1,1)到(n,n)的最大费用流就可以了。

AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int N=5510;
int n,k,g[55][55],s,t,d[N];
int head[N],nex[N<<2],fl[N<<2],w[N<<2],to[N<<2],tot=1;
struct node{
	int v,e;
}p[N];
inline void add(int a,int b,int c,int d){
	fl[++tot]=c; to[tot]=b; w[tot]=d; nex[tot]=head[a]; head[a]=tot;
	fl[++tot]=0; to[tot]=a; w[tot]=-d; nex[tot]=head[b]; head[b]=tot;
}
inline int num(int i,int j,int k){
	return (i-1)*n+j+k*n*n;//把坐标离散化 
}
bool spfa(){
	int vis[N]={0};	queue<int> q;	q.push(s);	vis[s]=1;
	memset(d,0xcf,sizeof d);	d[s]=0;
	while(q.size()){
		int u=q.front();	q.pop();	vis[u]=0;
		for(int i=head[u];i;i=nex[i]){
			if(fl[i]&&d[to[i]]<d[u]+w[i]){
				d[to[i]]=d[u]+w[i];
				p[to[i]].v=u;	p[to[i]].e=i;
				if(!vis[to[i]])	q.push(to[i]),vis[to[i]]=1;
			}
		}
	}
	return d[t]!=0xcfcfcfcf;//-INF
}
int EK(){
	int res=0;
	while(spfa()){
		int mi=0x3f3f3f3f;
		for(int i=t;i!=s;i=p[i].v)	mi=min(mi,fl[p[i].e]);
		for(int i=t;i!=s;i=p[i].v){
			fl[p[i].e]-=mi;	fl[p[i].e^1]+=mi;
		}
		res+=d[t]*mi;
	}
	return res;
}
int main(){
	cin>>n>>k;	s=num(1,1,0);	t=num(n,n,1);//s到t的费用流 
	for(int i=1;i<=n;i++)	for(int j=1;j<=n;j++)	cin>>g[i][j];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			add(num(i,j,0),num(i,j,1),1,g[i][j]);
			add(num(i,j,0),num(i,j,1),k-1,0);
			if(i<n)	add(num(i,j,1),num(i+1,j,0),k,0);
			if(j<n)	add(num(i,j,1),num(i,j+1,0),k,0);
		}
	}
	cout<<EK()<<endl;
	return 0;
}