【费用流】【CODEVS】1227 方格取数2
【算法】最小费用最大流(费用流)
【题解】
费用流:http://www.cnblogs.com/onioncyc/p/6496532.html
本题构图:
在有限的k次行走中尽可能多的拿到数字,明显的取舍问题,可以用网络流解决。
一共只有k次行走,因此流量至多为k。
而在起点到终点的所有最大流应该使价值最大化(取得数字最大),因此就可以构图了。
为每个节点x复制一个分点x’,从x向x'连一条容量1,费用-num[x]的边(限制数字只能取一次,代价变成负数就可以跑最小费用)
注意:因为每个点其实可以重复经过,但数字不能重复取,因此再从x向x'连一条容量k(相当于inf),费用0的边。
从x’向右和下连容量k,费用0的边。
S向第一格顶连容量k,费用0的边。
最后一格底向T连容量k,费用0的边。
构图完毕。
本题的核心是满足走k次(最大流)的前提下取的数字尽可能大(费用尽可能小),流只是前提,关键在每个节点那条带费用的边。
费用为负数的边越小当然越能吸引水流过去,最后就能跑出最小费用流。
---
注意数组范围!
spfa的vis只决定进不进队列,不妨碍松弛!
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int inf=0x3f3f3f3f,maxn=60,maxN=5100; int n,N,k,p[maxn][maxn],first[maxN],tot=1,d[maxN],q[1010],S,T,ans; bool vis[maxN]; struct edge{int from,v,flow,cost;}e[30000]; void insert(int u,int v,int flow,int cost) { tot++;e[tot].v=v;e[tot].flow=flow;e[tot].cost=cost;e[tot].from=first[u];first[u]=tot; tot++;e[tot].v=u;e[tot].flow=0;e[tot].cost=-cost;e[tot].from=first[v];first[v]=tot; } bool spfa() { memset(d,0x3f,4*(N+N+2)); memset(vis,0,N+N+2); int head=0,tail=1;q[0]=T; vis[T]=1;d[T]=0; while(head!=tail) { int x=q[head++];if(head>=1001)head=0; for(int i=first[x];i;i=e[i].from) if(e[i^1].flow&&d[x]+e[i^1].cost<d[e[i].v]) { d[e[i].v]=d[x]+e[i^1].cost; if(!vis[e[i].v]) { q[tail++]=e[i].v;if(tail>=1001)tail=0; vis[e[i].v]=1; } } vis[x]=0; } return d[S]<inf; } int dfs(int x,int a) { vis[x]=1; if(x==T||a==0)return a; int flow=0,f; for(int i=first[x];i;i=e[i].from) if(!vis[e[i].v]&&d[x]==e[i].cost+d[e[i].v]&&(f=dfs(e[i].v,min(a,e[i].flow)))>0) { e[i].flow-=f; e[i^1].flow+=f; ans+=e[i].cost*f; a-=f; flow+=f; if(a==0)break; } return flow; } int main() { scanf("%d%d",&n,&k); N=n*n;S=0,T=N+N+1; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { int x;p[i][j]=(i-1)*n+j; scanf("%d",&x); insert(p[i][j],p[i][j]+N,1,-x); insert(p[i][j],p[i][j]+N,k,0);//建立仅供行走的无价值边 } } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(j<n)insert(p[i][j]+N,p[i][j+1],k,0); if(i<n)insert(p[i][j]+N,p[i+1][j],k,0); } } insert(S,1,k,0);insert(N+N,T,k,0); ans=0; while(spfa()) { while(dfs(S,inf))memset(vis,0,N+N+2); } printf("%d",-ans); return 0; }
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,k,S,T; namespace nwf2{ const int maxn=5010,maxm=10010,inf=0x3f3f3f3f,N=5005; int first[maxn],cur[maxn],tot=1,q[maxn],d[maxn],ans=0;// bool vis[maxn]; struct edge{int v,f,c,from;}e[maxm*2]; void insert(int u,int v,int f,int c){ tot++;e[tot].v=v;e[tot].f=f;e[tot].c=c;e[tot].from=first[u];first[u]=tot; tot++;e[tot].v=u;e[tot].f=0;e[tot].c=-c;e[tot].from=first[v];first[v]=tot; } bool spfa(){ memset(d,0x3f,sizeof(d)); d[T]=0;vis[T]=1; int head=0,tail=1; q[head]=T; while(head!=tail){// int x=q[head++];if(head>N)head=0; for(int i=first[x];i;i=e[i].from) if(e[i^1].f&&d[x]+e[i^1].c<d[e[i].v]){// d[e[i].v]=d[x]+e[i^1].c; if(!vis[e[i].v]){ if(d[e[i].v]<d[q[head]]){head--;if(head<0)head=N;q[head]=e[i].v;} else{q[tail++]=e[i].v;if(tail>N)tail=0;} vis[e[i].v]=1; } } vis[x]=0; } return d[S]<inf; } int dfs(int x,int a){ if(x==T||a==0)return a; vis[x]=1; int flow=0,f; for(int& i=cur[x];i;i=e[i].from) if(!vis[e[i].v]&&d[e[i].v]+e[i].c==d[x]&&(f=dfs(e[i].v,min(e[i].f,a)))){// e[i].f-=f;e[i^1].f+=f; ans+=e[i].c*f; flow+=f;a-=f; if(a==0)break; } vis[x]=0; return flow; } int dinic(){ ans=0; while(spfa()){ for(int i=S;i<=T;i++)cur[i]=first[i]; dfs(S,inf); } return ans; } } int main(){ scanf("%d%d",&n,&k); S=0;T=5001; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ int u; scanf("%d",&u); nwf2::insert((i-1)*n+j,(i-1)*n+j+2500,1,-u); nwf2::insert((i-1)*n+j,(i-1)*n+j+2500,k-1,0); if(i!=n)nwf2::insert((i-1)*n+j+2500,i*n+j,k,0); if(j!=n)nwf2::insert((i-1)*n+j+2500,(i-1)*n+j+1,k,0); } } nwf2::insert(S,1,k,0);nwf2::insert(n*n+2500,T,k,0); printf("%d",-nwf2::dinic()); return 0; }