zoukankan      html  css  js  c++  java
  • 网络流

    HDU4862 这题说的是在一个n*m的格子内 你有K次机会选择起始的点,选择的点不能使以前用过的 ,然后选择后你可以往右 跳几步 也可以往下跳几步 但是只能要不往右 要不就往下 不能两个同时成立比如说 (1,1) 不能跳到(2,3);然后得到了 我们通过 拆点可以将他们分离开来保证只用一次,然后将拆的点的流量置为1 费用置为 负的无穷大,这样是为了勾引流量过来使得k次选择的点能够通过所有的点然后还要外加一条边是从源点流向汇点的这条边的流量为k 费用为0  这条边的目的是为了使得如果选择次数比k次来的小的时候可能达到最大值就不去影响那个最大值因为他保证可以使用少于k次的选择机会

    #include <cstdio>
    #include <cstdio>
    #include <string.h>
    #include <queue>
    using namespace std;
    const int MOD = 100000;
    const int INF = 100000000;
    const int maxn = 1000000;
    int first[300],next[maxn],u[maxn],v[maxn],w[maxn],num,cost[maxn],ed,st,map[200][200];
    int per[300],mark[300],dis[300],n,m;
    void add(int a, int b, int c, int d)
    {
          u[num] = a;
          v[num] = b;
          w[num] = c;
          cost[num] = d;
          next[num] = first[a];
          first[a] = num++;
          u[num] = b;
          v[num] = a;
          w[num] = 0;
          cost[num] = -d;
          next[num] = first[b];
          first[b]=num++;
    }
    int abs(int a){   return a>0?a:-a; }
    bool SPAF()
    {
          for(int i =0; i<= ed+1; ++i) dis[i]=INF,mark[i]=0,per[i]=-1;
          dis[ed+1]=0;
          queue<int> Q;
          Q.push(ed+1);
          while(!Q.empty())
          {
               int a = Q.front();
               Q.pop();
               mark[a]=0;
               for(int e =first[a]; e!=-1; e= next[e])
                if(dis[v[e]]>dis[u[e]]+cost[e]&&w[e]>0)
                {
                    dis[ v[ e ] ] = dis[ u[ e ] ] +cost[ e ];
                    per[v[e]] = e;
                    if(mark[v[e]]==0)
                        {
                              mark[v[e]] = 1;
                              Q.push(v[e]);
                        }
                }
          }
          return dis[ed]!=INF;
    }
    int EK()
    {
          int ans_COS=0   ;
          while(SPAF())
            {
                int flow =INF;
                for( int e=per[ed]; e!=-1; e=per[ u[e] ] )
                    if(flow>w[e]) flow=w[e];
                for(int e = per[ed]; e!=-1; e=per[u[e]])
                {
                      w[e]-=flow;
                      w[e^1]+=flow;
                }
                ans_COS+=dis[ed];
            }
          return (-(n*m*MOD+ans_COS))%MOD;
    }
    int main()
    {
        int t;
        scanf("%d",&t);
        for( int cas = 1 ; cas<=t; ++cas )
            {
                 int n,m,K;
                 scanf("%d%d%d",&n,&m,&K);
                 ed =n*m*2+1;
                 for(int i =0; i<=ed+1; ++i)
                     first[i]=-1;
                 st=num=0;
                 for(int i =1; i<=n; ++ i)
                    for(int j =1; j<=m; ++j)
                     {
                         scanf("%1d",&map[i][j]);
                         add( st, (i-1)*m+j , 1, 0);
                         add( ( i - 1 )*m+j, n*m+( i - 1 )*m+j, 1, -MOD );
                         add( n * m +( i - 1 )*m+j, ed, 1, 0 );
                     }
                     if(K<min(n,m)){
                        printf("Case %d : -1
    ",cas);
                        continue;
                     }
                 for(int i =1; i<=n; ++i)
                 for(int j=1; j<=m; ++j)
                    {
                          for(int k =i+1; k<=n; ++k)
                            add( n*m+(i-1)*m+j , (k-1)*m+j, 1 , abs(i-k)-1-(map[i][j]==map[k][j]?map[i][j]:0) );
                          for(int k= j+1; k<=m; ++k)
                            add( n*m+(i-1)*m+j , (i-1)*m+k, 1 , abs(j-k)-1-(map[i][j]==map[i][k]?map[i][j]:0) );
                    }
                 add(ed+1,st,K,0);
                 add(st,ed,K,0);
                 int ans = EK();
                 printf("Case %d : %d
    ",cas,ans);
            }
        return 0;
    }
    View Code

    题解是用 二部图做的最小K路径覆盖的模型,用费用流或者KM算法解决,构造二部图,X部有N*M个节点,源点向X部每个节点连一条边,流量1,费用0Y部有N*M个节点,每个节点向汇点连一条边,流量1,费用0,如果X部的节点x可以在一步之内到达Y部的节点y,那么就连边x->y,费用为从x格子到y格子的花费能量减去得到的能量,流量1,再在X部增加一个新的节点,表示可以从任意节点出发K次,源点向其连边,费用0,流量K,这个点向Y部每个点连边,费用0,流量1,最这个图跑最小费用最大流,如果满流就是存在解,反之不存在,最小费用的相反数就是可以获得的最大能量

    #include <cstdio>
    #include <string.h>
    #include <iostream>
    #include <queue>
    using namespace std;
    const int INF = 100000;
    const int maxn=100000;
    int first[300],next[maxn],u[maxn],v[maxn],cost[maxn],w[maxn],ed,num,st,n,m,k;
    int dis[300],mark[300],per[300],map[300][300];
    int abs(int a){ return a>0?a:-a; }
    void add(int a,int b, int flow, int cos)
    {
             u[ num ] = a;
             v[ num ] = b;
             w[ num ] = flow;
             cost[ num ] = cos;
             next[ num ] = first[a];
             first[ a ] = num++;
             u[ num ]=b;
             v[ num ]=a;
             w[ num ] = 0;
             cost[ num ] = -cos;
             next[ num ] = first[b];
             first[ b ]=num++;
    }
    bool SPAF()
    {
          for(int i =0; i<=ed+1; ++i)
            mark[i]=0,per[i]=-1,dis[i]=INF;
          queue<int>Q;
          Q.push(st);
          dis[st]=0;
          while(!Q.empty())
          {
                int a = Q.front();
                Q.pop();
                mark[a] =0;
                for(int e = first[a] ; e!=-1; e=next[e])
                    if( dis[v[e]]>dis[u[e]]+cost[e] && w[e]>0)
                    {
                          dis[ v[ e ] ] = dis[ u[ e ] ] +cost[ e ];
                          per[ v[ e ] ] = e;
                          if(mark[v[e]] == 0)
                            {
                                mark[ v[ e ] ] = 1;
                                Q.push(v[ e ]);
                            }
                    }
    
          }
          return dis[ed]!=INF;
    }
    int EK()
    {
          int Cos_t=0,Cos_flow=0;
          while(SPAF())
            {
                 int flow=INF;
                 for(int e = per[ed] ; e!=-1; e =per[u[e]])
                    if(flow>w[e]) flow=w[e];
                 for(int e = per[ed] ; e!=-1; e = per[u[e]] )
                 {
                      w[e]-=flow;
                      w[e^1]+=flow;
                 }
                 Cos_t+=dis[ed];
                 Cos_flow+=flow;
            }
            if(Cos_flow==n*m) return - Cos_t;
            else return -1;
    }
    int main()
    {
         int t;
         scanf("%d",&t);
         for( int cas = 1; cas <= t ; cas++ )
            {
                 scanf("%d%d%d",&n,&m,&k);
                 st=num=0;
                 ed= n*m*2 +1;
                 for(int i=0; i<=ed+1; ++i)
                     first[i]=-1;
                 for(int i=1; i<=n; ++i)
                   for(int j=1; j<=m; ++j)
                    {
                         scanf("%1d",&map[i][j]);
                         add( st, (i-1)*m+j, 1, 0);
                         add( n*m+(i-1)*m+j, ed, 1, 0);
                    }
                 for(int i =1; i<=n; ++i)
                     for(int j =1; j<=m; ++j)
                      {
                            for(int k =i+1; k<=n; ++k)
                              add( (i-1)*m+j, n*m+(k-1)*m+j, 1,abs(k-i)-1-(map[i][j]==map[k][j]?map[i][j]:0));
                            for(int k=j+1; k<=m; ++k)
                              add( (i-1)*m+j, n*m+(i-1)*m+k, 1,abs(k-j)-1-(map[i][j]==map[i][k]?map[i][j]:0));
                            add( ed+1, n*m+(i-1)*m+j, 1, 0);
                      }
                add(st,ed+1,k,0);
                int ans=EK();
                printf("Case %d : %d
    ",cas,ans);
            }
         return 0;
    }
    View Code

    的堆得得得得

  • 相关阅读:
    开放源码的对象关系映射工具ORM.NET 插入数据 Insert/Update Data
    开放源码的对象关系映射工具ORM.NET 快档开发入门 Quick Start
    .NET 动态脚本语言Script.NET 开发指南
    开放源码的对象关系映射工具ORM.NET 删除数据 Deleting Records using ORM.NET
    .NET Remoting过时了吗?为什么公司的项目还是选择用.NET Remoting,而不是WCF?
    开放源码的对象关系映射工具ORM.NET 查看和显示数据 View and Display data using ORM.NET
    开放源码的对象关系映射工具ORM.NET 查询表 调用存储过程 增加自定义代码
    技术人生:坚持,每日一博
    CQRS:CQRS + DDD + MDP 实现快速应用程序开发
    NodeJs:Happy代码生成器,重构了代码,更新了文档,完善了示例,欢迎下载使用
  • 原文地址:https://www.cnblogs.com/Opaser/p/3861735.html
Copyright © 2011-2022 走看看