题目大意
在 N∗N 的格子上,有一些数。从左上方走到右下方,每步只能向右或向下,走过的格子上的数累加到得分上之后变为0,走K次,问最大得分是多少。
分析
在不考虑这道题被分到费用流专题的情况下我们可能首先想到的是贪心,先求最长路,减去最长路上的数后再求最长路。但仔细思考后就会发现这个贪心并不正确。
费用流的思路:格子里的值可以看成是费用流中的花费,路线可以看成是流量。由于值是在格子上,所以需要进行拆点处理。格子拆为两个点a和b,a连向b两条边,一条容量为1,花费为负的格子的值(因为这道题是求的最大值),另一条容量INF花费0
建一个超级源点到起点,一个超级汇点到终点,除了这两条边的容量为k之外其他容量都为INF。
ps.对于拆点后的编号,我的代码中采用,原编号为i的点拆后变为i和i+N*N
代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXN=100005;
int N,K;
int grid[105][105];
int dis[MAXN];
int vis[MAXN];
int pre[MAXN];
struct Edge
{
int u;
int v;
int cap;
int cost;
int next;
}edge[4*MAXN];
int edgecount;
int head[MAXN];
void Init()
{
memset(head,-1,sizeof(head));
edgecount=0;
}
void Add_edge(int u,int v,int cap,int cost)
{
edge[edgecount].u=u;edge[edgecount].v=v;edge[edgecount].cap=cap;
edge[edgecount].cost=cost;edge[edgecount].next=head[u];
head[u]=edgecount++;
edge[edgecount].u=v;edge[edgecount].v=u;edge[edgecount].cap=0;
edge[edgecount].cost=-cost;edge[edgecount].next=head[v];
head[v]=edgecount++;
}
bool spfa(int s,int t ,int n)//0表示没有增广路
{
for(int i=0;i<=n;i++)
{
dis[i]=INF;
vis[i]=0;
pre[i]=-1;
}
dis[s]=0;
vis[s]=1;
queue<int> Q;
Q.push(s);
while(!Q.empty())
{
int u=Q.front();
Q.pop();
vis[u]=0;
for(int k=head[u];k!=-1;k=edge[k].next)
{
int v=edge[k].v;
int cost=edge[k].cost;
if(edge[k].cap && dis[v]> dis[u]+cost)
{
dis[v]=dis[u]+cost;
pre[v]=k;
if(!vis[v])
{
vis[v]=1;
Q.push(v);
}
}
}
}
if(dis[t]==INF)return 0;
return 1;
}
void MCMF(int s,int t,int n)
{
int minflow;
int mincost=0;
while(spfa(s,t,n))
{
minflow=INF;
for(int k=pre[t];k!=-1;k=pre[edge[k].u])
{
minflow=min(minflow,edge[k].cap);
}
for(int k=pre[t];k!=-1;k=pre[edge[k].u])
{
edge[k].cap-=minflow;
edge[k^1].cap+=minflow;
}
mincost+=dis[t];
//cout<<"mincost "<<mincost<<endl;
}
cout<<-mincost<<endl;
}
int get_num(int i,int j)//第i行第j列格子的编号
{
return (i-1)*N+j;
}
bool is_out(int x,int y)
{
if(x<1 || x>N || y<1 || y>N)return 1;
else return 0;
}
void Build()//建图
{
for(int i=1;i<=N;i++)
{
for(int j=1;j<=N;j++)
{
Add_edge(get_num(i,j),get_num(i,j)+N*N,1,-grid[i][j]);
Add_edge(get_num(i,j),get_num(i,j)+N*N,K,0);
if(!is_out(i+1,j))Add_edge(get_num(i,j)+N*N,get_num(i+1,j),K,0);//下
if(!is_out(i,j+1))Add_edge(get_num(i,j)+N*N,get_num(i,j+1),K,0);//右
}
}
Add_edge(0,1,K,0);
Add_edge(2*N*N,2*N*N+1,K,0);
}
int main()
{
while(scanf("%d%d",&N,&K)!=EOF)
{
for(int i=1;i<=N;i++)
{
for(int j=1;j<=N;j++)
{
scanf("%d",&grid[i][j]);
}
}
Init();
Build();
MCMF(0,2*N*N+1,2*N*N+2);
}
return 0;
}
/*
3 1
1 2 3
0 2 1
1 4 2
*/