• 2014湘潭全国邀请赛I题 Intervals /POJ 3680 / 在限制次数下取有权区间使权最大/小问题(费用流)


    先说POJ3680:给n个有权(权<10w)开区间(n<200),(区间最多数到10w)保证数轴上所有数最多被覆盖k次的情况下要求总权最大,输出最大权。

      思路:       限制的处理:s-->开始流量为k,要求总权最大,即费用最大,所以费用取负,最小费用最大流即可。对于输入区间[a,b]:w,添加边:a-->b,流量为1,费用为-w。

                      对于点i,i+1,添加边,费用为0,流量无穷。显然这种处理,限制了区间最多取k次,(流量控制),跑最大流能走添加的边尽量走,且越大越好(负数刚刚是最小费用),满足题意。但是TLE,因为w到10W,边10W,必然超时    ,所以点要处理,  所有点“压缩”,向前推进,只要存在的点,前一个向后一个连边即可,详见代码。


    再看湘潭的这题: 问题相反:给n个有权(权<10w)开区间(n<2000),(区间最多数到10的9次),保证区间【1-,m】最少被覆盖k次的情况下要求总权最小,输出最小权。

     思路:(感激zz1215的建图提示)    限制的处理:显然在出口处流量必需达到k才算有解。对于输入区间[a,b]:w,添加边:a-->b,流量为1,费用为w,但是这样处理,点都是离散的,根本没有体现连续性,

    不可能像上题那样建图(否则费用为0),所以:这样:点I向它前一个点连边,费用为0,流量为无穷,这样巧妙的解决了离散点问题。跑最小费用即可。显然,之前要处理点。


    #include<iostream>
    #include<queue>
    #include<cstdio>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int t=100000;
    int n,k;
    int e[300001][4];int head[100101];int nume=0;
    void inline adde(int from,int to,int w,int c)
    {
        e[nume][0]=to;e[nume][1]=head[from];head[from]=nume;
        e[nume][2]=w;e[nume++][3]=c;
        e[nume][0]=from;e[nume][1]=head[to];head[to]=nume;
        e[nume][2]=-w;e[nume++][3]=0;
    }
    int inq[111005];int d[110000];              //spfa
    bool spfa(int & sumcost)               //每次求费用
    {
        int pre[110005];
        int minf=inf;
        int prv[110005];
        for(int i=0;i<=t+1;i++)
          {
              inq[i]=0;d[i]=inf;
          }
        pre[0]=-1 ; prv[0]=-1;       //路径中,分别记录到点i的边,和i之前的点。(这题如果用矩阵建图要方便)
        queue<int>q;
        q.push(0);inq[0]=1;d[0]=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][3]>0&&e[i][2]+d[cur]<d[v])
                 {
                     d[v]=e[i][2]+d[cur];
                     prv[v]=cur;               //记录增广路
                     pre[v]=i;
                     if(!inq[v])
                     {
                         q.push(v);
                         inq[v]=1;
                     }
                 }
             }
        }
         if(d[t+1]==inf)return 0;       //无法增广
         int cur=t+1;                   //目的点
         while(cur!=0)              //取路径上最小残流量边作为流量增广
        {
            minf=e[pre[cur]][3]<minf?e[pre[cur]][3]:minf;
            cur=prv[cur];
        }
        cur=t+1;
        while(cur!=0)                 //增广,改流量
        {
             e[pre[cur]][3]-=minf;
             e[pre[cur]^1][3]+=minf;
             cur=prv[cur];
        }
       sumcost+=d[t+1]*minf;         //费用为单位费用(该路径下每条边单位流量之和)*流量
       return 1;
    }
    void mincost(int & sumcost)
    {
        while(spfa(sumcost)) ;     //无法增广为止
        return ;
    }
    int hash[100011];
    void clear()
    {
        nume=0;
        for(int i=0;i<=t+1;i++)
          {
              hash[i]=head[i]=-1;
          }
    }
    struct qujian
    {
        int a,b,w;
    };
    int main()
    {
       int T;
        scanf("%d",&T);
       for(int ii=1;ii<=T;ii++)
       {
           clear();
           cin>>n>>k;
           int a,b,w;
            vector<qujian>qq(n);
            vector<int>v;
           for(int i=0;i<n;i++)
           {
               scanf("%d%d%d",&a,&b,&w);
                hash[a]=hash[b]=1;            
                qq[i].a=a;qq[i].b=b;qq[i].w=w;
                adde(a,b,-w,1);
           }
           for(int i=0;i<100010;i++)     //处理“存在”的点
             if(hash[i]==1)
             {
                 v.push_back(i);
             }
           for(int i=0;i<v.size()-1;i++)   //“存在”的点连边
           {
               adde(v[i],v[i+1],0,k);
           }
           adde(v[v.size()-1],t,0,k);              //超级源汇点的边
           adde(0,v[0],0,k);adde(t,t+1,0,k);
             int sumcost=0;
           mincost(sumcost);
           cout<<-sumcost<<endl;              //相反数
       }
       return 0;
    }
    


    湘潭:

    #include<iostream>
    #include<queue>
    #include<algorithm>
    using namespace std;
    const int inf=0x3f3f3f3f;
    int n,k,m;
    int countv=0;
    int f[4009];
    void getf(int x)     // 把点1到10的9次(最多4000个点),压缩到4000以内,一一对应,以便建图。
    {
        if(x>m){f[countv]=x;return ;}    //大于m的数没用,相当于m。
        countv++;
        f[countv]=x;
    }
    int getv(int x)         //获取对应点
    {
        if(x>=m){return countv;}
        for(int i=1;i<=countv;i++)
        {
            if(f[i]==x)return i;
        }
    }
    int e[20001][4];int head[4005];int nume=0;
    void inline adde(int from,int to,int w,int c)
    {
        e[nume][0]=to;e[nume][1]=head[from];head[from]=nume;
        e[nume][2]=w;e[nume++][3]=c;
        e[nume][0]=from;e[nume][1]=head[to];head[to]=nume;
        e[nume][2]=-w;e[nume++][3]=0;
    }
    int inq[4005];int d[4005];              //spfa
    bool spfa(int & sumcost,int &sumflow)               //每次求费用
    {
        int pre[4005];
        int minf=inf;
        int prv[4005];
        for(int i=0;i<=countv+4;i++)
          {
              inq[i]=0; d[i]=inf;
          }
        pre[0]=-1 ; prv[0]=-1;       //路径中,分别记录到点i的边,和i之前的点。(这题如果用矩阵建图要方便)
        queue<int>q;
        q.push(0);inq[0]=1;d[0]=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][3]>0&&e[i][2]+d[cur]<d[v])
                 {
                     d[v]=e[i][2]+d[cur];
                     prv[v]=cur;               //记录增广路
                     pre[v]=i;
                     if(!inq[v])
                     {
                         q.push(v);
                         inq[v]=1;
                     }
                 }
             }
        }
         if(d[countv+1]==inf)return 0;       //无法增广
         int cur=countv+1;                   //目的点
         while(cur!=0)              //取路径上最小残流量边作为流量增广
        {
            minf=e[pre[cur]][3]<minf?e[pre[cur]][3]:minf;
            cur=prv[cur];
        }
        cur=countv+1;
        while(cur!=0)                 //增广,改流量
        {
             e[pre[cur]][3]-=minf;
             e[pre[cur]^1][3]+=minf;
             cur=prv[cur];
        }
       sumcost+=d[countv+1]*minf;         //费用为单位费用(该路径下每条边单位流量之和)*流量
       sumflow+=minf;
       return 1;
    }
    void mincost(int & sumcost,int & sumflow)
    {
        while(spfa(sumcost,sumflow)) ;     //无法增广为止
        return ;
    }
    void clear()
    {
        nume=countv=0;
        for(int i=0;i<=4003;i++)
          {
              head[i]=-1;
              f[i]=0;
          }
    }
    struct qujian
    {
        int a,b,w;
    };
    int main()
    {
       int T;
       cin>>T;
       for(int ii=1;ii<=T;ii++)
       {
           clear();
           cin>>n>>k>>m;
           int a,b,w;
           vector<int>v;
           vector<qujian>qq(n);
           for(int i=0;i<n;i++)
           {
                cin>>a>>b>>w;
                v.push_back(a);
                v.push_back(b);
                qq[i].a=a;qq[i].b=b;qq[i].w=w;
           }
           sort(v.begin(),v.end());       //从小到大处理点
           for(int i=0;i<v.size();i++)
           {
               getf(v[i]);
           }
           for(int i=0;i<n;i++)
           {
                int t1=getv(qq[i].a);
                int t2=getv(qq[i].b);
                adde(t1,t2,qq[i].w,1);  //注意点:若使用adde(getv(a),getv(b),w,1)参数是从右往左开始赋值传递的!!!
           }
             for(int i=1;i<countv;i++)
           {
                adde(i+1,i,0,inf);  //注意点:若使用adde(getv(a),getv(b),w,1)参数是从右往左开始赋值传递的!!!
           }
           adde(0,1,0,inf);adde(countv,countv+1,0,k);
            int sumcost=0;  int sumflow=0;
           mincost(sumcost,sumflow);
           cout<<"Case "<<ii<<": ";
           if(sumflow!=k)       //到不了k,无解
           {
               cout<<-1<<endl;
           }
           else
           {
               cout<<sumcost<<endl;
           }
       }
       return 0;
    }
    






  • 相关阅读:
    kolla多节点部署openstack
    归并排序
    gitlab ci/cd
    微信、支付宝个人收款码不能用于经营收款 z
    微信小程序弹出和隐藏遮罩层动画以及五星评分
    centos7 安装 nginx
    netty+websocket模式下token身份验证遇到的问题
    windows 截图 win+shift+s
    linux下 "chmod 777" 中777这个数字是怎么出来的
    nginx四层转发,访问内网mysql数据库
  • 原文地址:https://www.cnblogs.com/yezekun/p/3925789.html
Copyright © 2020-2023  润新知