POJ 3422 Kaka's Matrix Travels【最大费用流】
题意: 给出一个 n*n的数字矩阵,从左上角走到右下角每次只能向下或向右走,且每个格子中的数字只能取一次,问走k次之后可以取到的最大的数值和为多少。
分析: 比较经典的网络流解DP的题目,可以用费用流求解,
建图:
将每个割格点拆分成两个点 v(入点) ,v`(出点),在v 和v`之间先连一条容量为 1 ,费用为格子数值的边,再连一条容量为无穷大费用为0的边,
这样既可以保证每个数只被取一次,又能保证可以多次经过这个数所在的格子
建立源点 s = 0,在 s 和 最左上角的格子的入点之间连一条容量为 K,费用为0 的边
建立汇点 t = n*n*2+1,在 t 和最右下角的格子的出点之间连一条容量为 K,费用为 0 的边。
求最大费用流...
#include<stdio.h> #include<string.h> #include<stdlib.h> #define clr(x)memset(x,0,sizeof(x)) #define INF 0x1f1f1f1f #define min(a,b)(a)<(b)?(a):(b) #define max(a,b)(a)>(b)?(a):(b) struct node { int next,from,to,w,c; }e[5500000]; int tot; int head[5005]; void add(int st,int u,int wi,int flow) { e[tot].from=st; e[tot].to=u; e[tot].w=wi; e[tot].c=flow; e[tot].next=head[st]; head[st]=tot++; e[tot].from=u; e[tot].to=st; e[tot].w=-wi; e[tot].c=0; e[tot].next=head[u]; head[u]=tot++; } int q[10000000]; int pre[5005]; int dis[5005]; int v[5005]; int s,t; int g[55][55]; int spfa() { int i,x,front,rear,k; for(i=0;i<=t;i++) dis[i]=-1; // 初始为 -1 clr(v); front=rear=0; q[rear++]=s; pre[s]=-1; dis[s]=0; v[s]=1; while(front<rear) { x=q[front++]; v[x]=0; for(i=head[x];i!=-1;i=e[i].next) { k=e[i].to; if(e[i].c&&dis[x]+e[i].w>dis[k]) // 最大费用 { dis[k]=dis[x]+e[i].w; pre[k]=i; if(!v[k]) { v[k]=1; q[rear++]=k; } } } } if(dis[t]!=-1) return 1; return 0; } int costflow() { int flow=0,u,minf=INF; while(spfa()) { minf=INF; for(u=pre[t];u!=-1;u=pre[e[u].from]) minf=min(minf,e[u].c); for(u=pre[t];u!=-1;u=pre[e[u].from]) { e[u].c-=minf; e[u^1].c+=minf; flow+=e[u].w*minf; } } return flow; } int main() { int i,j,n,m; while(scanf("%d%d",&n,&m)!=EOF) { for(i=1;i<=n;i++) for(j=1;j<=n;j++) scanf("%d",&g[i][j]); s=0; t=2*n*n+1; tot=0; memset(head,-1,sizeof(head)); add(s,1,0,m); for(i=1;i<=n;i++) for(j=1;j<=n;j++) { add((i-1)*n+j,(i-1)*n+j+n*n,g[i][j],1); add((i-1)*n+j,(i-1)*n+j+n*n,0,INF); if(j<n) add((i-1)*n+j+n*n,(i-1)*n+j+1,0,INF); if(i<n) add((i-1)*n+j+n*n,i*n+j,0,INF); } add(2*n*n,t,0,m); printf("%d\n",costflow()); } return 0; }