题目链接: poj 3422
题目大意: 给出NxN的数字矩阵,左上角走到右下角(只能向右或向下走),sum记录走过点值的和
走过的点值置为0,问走K次,求sum的最大值
解题思路: 开始想到的是K次最长路,结果WA了
因为每次走最长路会导致下次走的时候不是最优解
建立费用流模型,把每个顶点T,分成两个顶点T和T‘‘
因为要限制每个顶点的值只加一次,T->T‘‘容量为1,费用为T的值:
1.建立超级源点,源点指向顶点T1,容量为K,费用0
2.建立超级汇点,顶点T(2*n)指向汇点,容量为K,费用为0
3.每个顶点拆分为两个点,加两条边 (Ti指向Ti‘‘,容量为1,费用为T的值)
(Ti指向Ti‘‘,容量为K,费用为0)
4.若能从T1走到T2,则T1‘‘连T2,容量为K,费用为0
然后求一次最小费用最大流就是最终结果
代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX 5100 #define INF 0x3f3f3f3f #define Min(a,b) (a<b?a:b) int S,E,vis[MAX],pre[MAX],Index,dis[MAX],path[MAX],que[2000000],Fi[MAX*100]; int Fx[2][2]={{1,0},{0,1}}; struct snode{ int r,to,c,w,next; }Edge[MAX*100]; void Add_edge(int a,int b,int c,int d) { d=-d; //等价于负边求最小费用流 Edge[Index].to=b,Edge[Index].c=c,Edge[Index].w=d; Edge[Index].next=pre[a],Edge[Index].r=Index+1,pre[a]=Index++; Edge[Index].to=a,Edge[Index].c=0,Edge[Index].w=-d; //反向边C为0,费用为-d Edge[Index].next=pre[b],Edge[Index].r=Index-1,pre[b]=Index++; } bool BFS() { int i,s,e,v1,v2; memset(vis,0,sizeof(vis)); memset(path,-1,sizeof(path)); memset(dis,INF,sizeof(dis)); s=e=0; que[s++]=S; vis[S]=1; dis[S]=0; while(s!=e) { v1=que[e++]; vis[v1]=0; for(i=pre[v1];i!=-1;i=Edge[i].next) { v2=Edge[i].to; if(Edge[i].c&&(dis[v2]==INF||dis[v2]>dis[v1]+Edge[i].w)) //<是最大费用流,>是最小费用流 { dis[v2]=dis[v1]+Edge[i].w; path[v2]=v1,Fi[v2]=i; if(!vis[v2]) { vis[v2]=1; que[s++]=v2; } } } } if(dis[E]==INF) return false; return true; } int Find() { int i,sum=0,MIN=INF; for(i=E;i!=-1;i=path[i]) { if(path[i]!=-1) MIN=Min(MIN,Edge[Fi[i]].c); } for(i=E;i!=-1;i=path[i]) { if(path[i]!=-1) { Edge[Fi[i]].c-=MIN; Edge[Edge[Fi[i]].r].c+=MIN; sum=sum+(Edge[Fi[i]].w*MIN); } } return sum; } int Solve() { int sum=0; while(BFS()) sum+=Find(); return sum; } int main() { int i,j,n,k,a,b,c,d,num[53][53]; while(scanf("%d%d",&n,&k)!=EOF) { Index=0; memset(pre,-1,sizeof(pre)); for(i=1;i<=n;i++) for(j=1;j<=n;j++) scanf("%d",&num[i][j]); S=0,E=n*n*2+1; Add_edge(S,1,k,0); //左边 Add_edge(E-1,E,k,0); //右边 int v1,v2,j1,x,y; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { v1=(i-1)*n+j; Add_edge(v1*2-1,v1*2,1,num[i][j]); //分点边1 Add_edge(v1*2-1,v1*2,k,0); //分点边2 for(j1=0;j1<2;j1++) { x=i+Fx[j1][0],y=j+Fx[j1][1]; if(x>=1&&x<=n&&y>=1&&y<=n) { v2=(x-1)*n+y; Add_edge(v1*2,v2*2-1,k,0); //各点间 } } } } printf("%d\n",-Solve()); } return 0; }