【算法】最小费用最大流(费用流)

【题解】

费用流: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;
}
View Code

相关文章:

  • 2022-01-26
  • 2021-08-24
  • 2021-06-17
  • 2022-12-23
  • 2021-05-26
  • 2022-12-23
  • 2021-11-21
  • 2021-09-15
猜你喜欢
  • 2021-06-24
  • 2022-12-23
  • 2021-08-05
  • 2022-12-23
  • 2021-07-19
  • 2021-10-23
  • 2022-12-23
相关资源
相似解决方案