zoukankan      html  css  js  c++  java
  • hdu3947 给一些已知(需费用)路径去覆盖一些边 //预先加灌法费用流

    River Problem

    题意:一个有向树(河流),只有一个汇点1,每条边只有一个出度。有些河道有污染指数xi,必需要治理,有m段路径,可以去覆盖这些,每被覆盖一次,xi降低响应值。

    :即 给出一些边必需要覆盖的次数,用m段路径去覆盖,每次覆盖有相应费用,求最小费用。

    思路:这题被誉为难题,给一个网络流,给出一些边的流量下界,以及给用某些路段流量去流满足要求。这里与正常网络流相悖,是wi>=xi,所以用:

    预先加灌法:(预先灌入大流量(相应干道大小灌溉相应流量),要覆盖的减掉,使达不到预期流量,而用添加费用来补充,恰好跑最小费用最大流)

         相应河道流量为U-wi(wi为要覆盖次数),再给的路径直接连边。如果没有wi,那么必然最大流为 U*主干道流量。应该有这些污染指数,所以在某些边流量变窄了!,再没有花费路径补充流量的情况下,比如达不到最大流,就无解,这样起到补充作用。

    取之官方思路:


    这里u取wi最大值略大(50)即可.

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int maxv=301,maxe=10001;
    int nume=0;int head[maxv];int e[maxe][4];
    void inline adde(int i,int j,int c,int w)         //网络流图
    {  
        e[nume][0]=j;e[nume][1]=head[i];head[i]=nume;
        e[nume][2]=c;e[nume++][3]=w;
        e[nume][0]=i;e[nume][1]=head[j];head[j]=nume;
        e[nume][2]=0;e[nume++][3]=-w;
    }
    int nume2=0;int head2[maxv];int e2[maxe][4];
    void inline adde2(int i,int j,int c,int w)              //原图
    {
        e2[nume2][0]=j;e2[nume2][1]=head2[i];head2[i]=nume2;
        e2[nume2][2]=c;e2[nume2++][3]=w;
    }
    int numfa[maxv];                              //即dp[i]:i的边干道宽度/包含叶子数
    int outd[maxv];
    int n,m;int ss,tt;
    int inq[maxv];int d[maxv]; int pre[maxv];int prv[maxv];
    bool spfa(int &sum,int &sumflow)
    {
        for(int i=0;i<=n;i++)
        {
            inq[i]=0;
            d[i]=inf;
        }
        queue<int>q;
        q.push(ss);
        inq[ss]=1;
        d[ss]=0;
        while(!q.empty())
        {
            int cur=q.front();
            q.pop();
            inq[cur]=0;
            for(int i=head[cur];i!=-1;i=e[i][1])
            {
                int v=e[i][0];
                if(e[i][2]>0&&d[cur]+e[i][3]<d[v])
                {
                    d[v]=d[cur]+e[i][3];
                    pre[v]=i;
                    prv[v]=cur;
                    if(!inq[v])
                    {
                        q.push(v);
                        inq[v]=1;
                    }
                }
            }
        }
        if(d[tt]==inf)return 0;
        int cur=tt;int minf=inf;
       while(cur!=ss)
        {
            int fe=pre[cur];
            minf=e[fe][2]<minf?e[fe][2]:minf;
            cur=prv[cur];
        }
         cur=tt;
        while(cur!=ss)
        {
            e[pre[cur]][2]-=minf;
            e[pre[cur]^1][2]+=minf;
            cur=prv[cur];
        }
        sum+=d[tt]*minf;
        sumflow+=minf;
        return 1;
    }
    int sumcost(int &sumflow)
    {
        int sum=0;
        while(spfa(sum,sumflow));
        return sum;
    }
    int dfs_getnumfa(int u)                 // 记录出i所包含的叶子数(源点数)
    {
        if(u==0)return 1;
        for(int i=head2[u];i!=-1;i=e2[i][1])
        {
               int v=e2[i][0];
                 numfa[u]+=dfs_getnumfa(v);
        }
        return numfa[u];
    }
    void read_build()
    {
           scanf("%d",&n);
           int aa,bb,cc,ww;
            for(int i=0;i<n-1;i++)
            {
                scanf("%d%d%d",&aa,&bb,&cc);
                adde2(bb,aa,0,cc);         //反向建树,原来的是倒的树
                outd[bb]++;
            }
             for(int i=1;i<=n;i++)
            {
                if(outd[i]==0)            //源点
                {
                    adde2(i,0,0,0);
                }
            }
            numfa[0]=1;
            dfs_getnumfa(1);
          for(int i=0;i<=n;i++)
          {
              for(int j=head2[i];j!=-1;j=e2[j][1])
              {
                  int v=e2[j][0];
                  adde(v,i,numfa[v]*50-e2[j][3],0);
              }
          }
          scanf("%d",&m);
          for(int i=0;i<m;i++)
          {
              scanf("%d%d%d%d",&aa,&bb,&cc,&ww);
              adde(aa,bb,cc,ww);
          }
    }
    void init()
    {
        nume=0;
        memset(head,-1,sizeof(head));
        nume2=0;
        memset(head2,-1,sizeof(head2));
        memset(numfa,0,sizeof(numfa));
        memset(outd,0,sizeof(outd));
        ss=0;tt=1;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        for(int ii=1;ii<=T;ii++)
        {
            init();
            read_build();
            int sumflow=0;
            int ans=sumcost(sumflow);
            if(sumflow!=numfa[1]*50)
              printf("Case #%d: -1
    ",ii);
            else
              printf("Case #%d: %d
    ",ii,ans);
        }
        return 0;
    }
    



  • 相关阅读:
    OpenCV程序在生产环境中运行
    C#调用C++导出(dllexport)方法
    IIS7.5 GZip配置
    wcf学习笔记--初识wcf
    Greenplum installation guide
    Cloudera 5.8.2 Installation guide
    WPF DataGrid 合并单元格
    wpf DataGrid CheckBox列全选
    WPF button 圆角制作
    WPF passwordbox 圆角制作
  • 原文地址:https://www.cnblogs.com/yezekun/p/3925774.html
Copyright © 2011-2022 走看看