题目大意:矩阵中每次只能向下走或者向右走。走K次问能得到的最大的和是多少。
解题报告:一开始以为是普通的贪心,每次取上边或者是左边中较大的数,取K次。当然,WA了。Discuss里也有讨论,例如下面这种情况:
1 1 0
4 3 3
5 0 1
贪心的话,第一次取 1,4,3,3,1,第二次取5。当然,最优解是取1,1,3,3,1,和4,5。
当然,这种情况实在不知道怎么做,那就搜吧。学习了最小费用最大流。
所谓最小费用最大流,即每次选择费用最小的增广路径进行增广,直到没有增广路径为止。
寻找最小路径可以用最短路算法。因为可能有负权边,所以使用Bellman算法或者SPFA。
最小费用最大流与最大费用最大流实际上是一样的。本题求的是最大费用最大流。
注意建图。要拆点。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,ans;
const int maxV=5010;
const int maxE=20010;
int first[maxV],vv[maxE],cap[maxE],cost[maxE],nxt[maxE];
int dis[maxV],pre[maxV],que[maxV];
bool vis[maxV];
bool spfa()
{
memset(dis,-1,sizeof(dis));
memset(vis,0,sizeof(vis));
int top=1,bot=0;
que[0]=0;
vis[0]=true;
dis[0]=0;
while(top!=bot)
{
int u=que[bot++];
if(bot==maxV) bot=0;
vis[u]=false;
for(int e=first[u];e;e=nxt[e])
if(cap[e] && dis[vv[e]]<dis[u]+cost[e])
{
int v=vv[e];
dis[v]=dis[u]+cost[e];
pre[v]=e;
if(!vis[v])
{
vis[v]=true;
que[top++]=v;
if(top==maxV) top=0;
}
}
}
return dis[n]>0;
}
void end()
{
for(int u=n,e;u;u=vv[e^1])
{
e=pre[u];
cap[e]-=1;
cap[e^1]+=1;
ans+=cost[e];
}
}
int e=2;
void addEdge(int u,int v,int ca,int co)
{
nxt[e]=first[u],vv[e]=v,cap[e]=ca,cost[e]=co,first[u]=e++;
nxt[e]=first[v],vv[e]=u,cap[e]=0,cost[e]=-co,first[v]=e++;
}
int main()
{
int N,K;
int mp[52][52];
scanf("%d%d",&N,&K);
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
scanf("%d",&mp[i][j]);
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
{
int a=(i-1)*N+j;
int b=a+N*N;
addEdge(a,b,1,mp[i][j]);
addEdge(a,b,K,0);
if(i<N) addEdge(b,a+N,K,0);
if(j<N) addEdge(b,a+1,K,0);
}
n=2*N*N+1;
addEdge(0,1,K,0);
addEdge(n-1,n,K,0);
ans=0;
while(spfa()) end();
printf("%d\n",ans);
}