题目:
给个n*n的带正权矩阵,k次从(1,1)走到(n,n),每个格子的权值只能获得一次,每次只能向右或下走
问获得最大权值
题解:
求最大权值可以把权值变成负的求最小
然后考虑怎么约束每个格子的权值,
把格子拆成两个点,连两条边:一条容量为1,花费为-权值,一条容量为INF,花费为0
S到第一个格子,连容量为k,最后一个格子的二号点到T,容量为k
这样就可以限制每个点只有走一次的时候能获得权值,
跑最小费用最大流
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define N 5010
#define INF 1000000
using namespace std;
int ecnt=1,vis[N],dist[N],n,m,S,T,ans,head[N],k,w[N][N];
deque <int> q;
inline int get(int x,int y)
{
return n*(x-1)+y;
}
struct adj
{
int nxt,v,w,c;
}e[N*N];
inline void add(int u,int v,int w,int c)
{
e[++ecnt].v=v,e[ecnt].w=w,e[ecnt].c=c,e[ecnt].nxt=head[u],head[u]=ecnt;
e[++ecnt].v=u,e[ecnt].w=0,e[ecnt].c=-c,e[ecnt].nxt=head[v],head[v]=ecnt;
}
inline int spfa(int s,int t)
{
int v;
memset(vis,0,sizeof(vis));
for (int i=s;i<=t;i++) dist[i]=INF;
dist[t]=0,vis[t]=1;
q.push_back(t);
while (!q.empty())
{
int u=q.front();q.pop_front();
for (int i=head[u];i;i=e[i].nxt)
if (e[i^1].w>0 && dist[v=e[i].v]>dist[u]-e[i].c)
{
dist[v]=dist[u]-e[i].c;
if (!vis[v])
{
vis[v]=1;
if (!q.empty() && dist[v]<dist[q.front()])
q.push_front(v);
else
q.push_back(v);
}
}
vis[u]=0;
}
return dist[s]<INF;
}
inline int dfs(int x,int flow)
{
// puts("DFS");
if (x==T)
return vis[T]=1,flow;
int used=0,tmp,v;
vis[x]=1;
for (int i=head[x];i;i=e[i].nxt)
if (!vis[v=e[i].v] && e[i].w>0 && dist[x]-e[i].c==dist[v])
{
tmp=dfs(v,min(e[i].w,flow-used));
if (tmp>0)
ans+=tmp*e[i].c,e[i].w-=tmp,e[i^1].w+=tmp,used+=tmp;
if (used==flow) break;
}
return used;
}
inline int CostFlow()
{
int Flow=0;
while (spfa(S,T))
{
vis[T]=1;
while (vis[T])
{
memset(vis,0,sizeof(vis));
Flow+=dfs(S,INF);
}
}
return Flow;
}
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
scanf("%d",&w[i][j]);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
int num=get(i,j);
add(num,num+n*n,1,-w[i][j]);
add(num,num+n*n,k-1,0);
if (i<n) add(n*n+num,get(i+1,j),k,0);
if (j<n) add(n*n+num,get(i,j+1),k,0);
}
S=0,T=n*n+n*n+1;
add(S,1,k,0);
add(n*n+get(n,n),T,k,0);
CostFlow();
printf("%d",-ans);
return 0;
}