zoukankan      html  css  js  c++  java
  • 【费用流】【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;
    }
    View Code
    #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;
    }
    View Code
  • 相关阅读:
    【题解】一本通例题 S-Nim
    【题解】一本通例题 取石子游戏
    【题解】[USACO09NOV]A Coin Game S
    【题解】取火柴游戏
    【题解】CF375D Tree and Queries
    Linux sudo用户提权与日志审计
    No space left on device
    CentOS下多种方法显示文本行号
    Python之行-01之初识python
    约瑟夫问题
  • 原文地址:https://www.cnblogs.com/onioncyc/p/6445896.html
Copyright © 2011-2022 走看看