题目:
给个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; }