费用流问题,只不过这次是最大费用,建边的时候权值取相反数,按最小费用流求完之后在输出负值就行。
题目大意:
Kaka有一个矩阵表格,行从1到n,列从1到n,Kaka从(1,1)开始,向(n,n)走,每一个格子里都有钱w,只能向下或向右走,走到那个格子就可以拿到那个格子里的钱。问走k次最多能拿多少钱。
这个题建图的时候需要拆点。每个格子都是一个点。把一个点拆成两个,两个点之间有两条路,一个容量为1,权值为那个格子的金钱数。另一条路容量为k-1,权值为0。因为走了一次钱捡起来之后就没钱了。
我的代码中为了处理方便,把超级原点设为了n*n*2,超级汇点设为了n*n*2+1。请大家在阅读中注意。
下面是代码:
#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;
const int inf=1<<30,M=5005;
int n,m,map1[55][55],head[M],cnt,pre[M],dis[M];
bool vis[M];
struct node
{
int u,v,w,f,next;
} edge[100005];
int min(int a,int b)
{
if(a>b)
{
a=b;
}
return a;
}
void add(int u,int v,int w,int f)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].w=w;//正向边权值为正
edge[cnt].f=f;//正向边流量为f
edge[cnt].next=head[u];
head[u]=cnt;
cnt++;
edge[cnt].u=v;
edge[cnt].v=u;
edge[cnt].w=-w;//正向边权值为负
edge[cnt].f=0;//流量为零
edge[cnt].next=head[v];
head[v]=cnt;
cnt++;
}
bool spfa() //基于邻接表的SPFA算法
{
int i;
for(i=0; i<=n*n*2+1; i++) //初始化
{
pre[i]=-1;
dis[i]=inf;
vis[i]=false;
}
queue <int > q;
dis[n*n*2]=0;
vis[n*n*2]=true;
q.push(n*n*2);
while(!q.empty())
{
int t=q.front();
q.pop();
i=head[t];
vis[t]=false;
while(i!=-1)
{
if(edge[i].f>0&&dis[edge[i].v]>dis[t]+edge[i].w)
{
dis[edge[i].v]=dis[t]+edge[i].w;
pre[edge[i].v]=i;
if(!vis[edge[i].v])
{
vis[edge[i].v]=true;
q.push(edge[i].v);
}
}
i=edge[i].next;
}
}
if(pre[n*n*2+1]==-1)//如果不在最短路中 代表着最短路寻找失败
{
return false;
}
return true;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
int i,j;
cnt=0;
for(i=1; i<=n; i++)
{
for(j=1; j<=n; j++)
{
scanf("%d",&map1[i][j]);
}
}
memset(head,-1,sizeof(head));
for(i=1; i<=n; i++) //每一点的拆点建边操作
{
for(j=1; j<=n; j++)
{
int u=(i-1)*n+j-1;
add(u*2,u*2+1,-map1[i][j],1); //权值为负值
add(u*2,u*2+1,0,m-1);
}
}
for(i=1; i<=n; i++)//向下移动建边
{
for(j=1; j<n; j++)
{
int u=(i-1)*n+j-1;
add(u*2+1,(u+1)*2,0,m);
}
}
for(i=1; i<n; i++)//向右移动建边
{
for(j=1; j<=n; j++)
{
int u=(i-1)*n+j-1;
add(u*2+1,(u+n)*2,0,m);
}
}
add(n*n*2,0,0,m);//超级原点建边
add(n*n*2-1,n*n*2+1,0,m);//超级汇点建边
int ans=0;
while(spfa()) //如果最短增广路寻找成功
{
int max1=inf;
int p=pre[n*n*2+1];//初始化P指针
while(p!=-1) //寻找关键流量
{
max1=min(max1,edge[p].f);
p=pre[edge[p].u];
}
p=pre[n*n*2+1];
while(p!=-1) //修改流量
{
edge[p].f-=max1;
edge[p^1].f+=max1;
ans+=max1*edge[p].w;
p=pre[edge[p].u];
}
}
printf("%d\n",-ans);
}
return 0;
}