• [网络流24题(3/24)] 最长k可重区间集问题(洛谷P3358)


    传送门

    分析:

    这是一个非常经典的费用流的模型。

    首先因为题目中限制我们每个点最多只能选取(k)次,因此,因为会有(k)次的限制,因此我们不妨用最大流进行限流,即我们将源点拆成两个点(S_0)以及(S_1),从(S_0)点向(S_1)点连一条流量为k,费用为(0)的边。代表最多有大小为(k)的流经过所有的边。这样我们就能够保证,最终通过终点的流量被限死在k,而又因为要求的是最大价值,因此我们需要求的是最大费用最大流,这个我们只需要将价值变成负数。

    因为最多会有(n)对区间,(2n)个点,因此我们考虑将着(2n)个点离散化。离散化后,我们将这离散化后的(2n)个点中离散化后相邻的点都连一条流量为(inf),费用为(0)的边,代表相邻的点都可以互相到达。之后我们再将之前的(n)对区间的每一个区间([l,r]),将点(l)和点(r)之间连一条费用为(-len_{lr}),流量为(1)的边,代表如果要选取当前区间,则将会花费(-len{lr})的费用以及区间中的所有点都将占用(1)点流量。建立好图之后,我们跑一边最小费用最大流之后取最小费用的相反数即可。

    代码:

    // luogu-judger-enable-o2
    #include <bits/stdc++.h>
    #define maxn 1006
    #define maxm 10005
    using namespace std;
    int head[maxn],cnt=0;
    int dis[maxn],vis[maxn],sp,ep,maxflow,cost;
    int n,k;
    const int INF=0x3f3f3f3f;
    struct Node{
        int to,next,val,cost;
    }q[maxm<<1];
    int L[maxn],R[maxn];
    vector<int>vec;
    void init(){
        memset(head,-1, sizeof(head));
        cnt=2;
        maxflow=cost=0;
    }
    void addedge(int from,int to,int val,int cost){
        q[cnt].to=to;
        q[cnt].next=head[from];
        q[cnt].val=val;
        q[cnt].cost=cost;
        head[from]=cnt++;
    }
    void add_edge(int from,int to,int val,int cost){
        addedge(from,to,val,cost);
        addedge(to,from,0,-cost);
    }
    bool spfa(){
        memset(vis,0,sizeof(vis));
        memset(dis,0x3f,sizeof(dis));
        dis[sp]=0;
        vis[sp]=1;
        queue<int>que;
        que.push(sp);
        while(!que.empty()){
            int x=que.front();
            que.pop();
            vis[x]=0;
            for(int i=head[x];i!=-1;i=q[i].next){
                int to=q[i].to;
                if(dis[to]>dis[x]+q[i].cost&&q[i].val){
                    dis[to]=dis[x]+q[i].cost;
                    if(!vis[to]){
                        que.push(to);
                        vis[to]=1;
                    }
                }
            }
        }
        return dis[ep]!=0x3f3f3f3f;
    }
    int dfs(int x,int flow){
        if(x==ep){
            vis[ep]=1;
            maxflow+=flow;
            return flow;
        }//可以到达t,加流
        int used=0;//该条路径可用流量
        vis[x]=1;
        for(int i=head[x];i!=-1;i=q[i].next){
            int to=q[i].to;
            if((vis[to]==0||to==ep)&&q[i].val!=0&&dis[to]==dis[x]+q[i].cost){
                int minflow=dfs(to,min(flow-used,q[i].val));
                if(minflow!=0){
                    cost+=q[i].cost*minflow;
                    q[i].val-=minflow;
                    q[i^1].val+=minflow;
                    used+=minflow;
                }
                //可以到达t,加费用,扣流量
                if(used==flow)break;
            }
        }
        return used;
    }
    int mincostmaxflow(){
        while(spfa()){
            vis[ep]=1;
            while(vis[ep]){
                memset(vis,0,sizeof(vis));
                dfs(sp,INF);
            }
        }
        return maxflow;
    }
    int main()
    {
        scanf("%d%d",&n,&k);
        init();
        for(int i=1;i<=n;i++){
            scanf("%d%d",&L[i],&R[i]);
            if(L[i]>R[i]) swap(L[i],R[i]);
            vec.push_back(L[i]);
            vec.push_back(R[i]);
        }
        sort(vec.begin(),vec.end());
        int sz=vec.size();
        sp=sz+1,ep=sz+2;
        add_edge(sp,1,k,0);
        add_edge(sz,ep,k,0);
        for(int i=1;i<sz;i++) add_edge(i,i+1,k,0);
        for(int i=1;i<=n;i++){
            int l=lower_bound(vec.begin(),vec.end(),L[i])-vec.begin();
            int r=lower_bound(vec.begin(),vec.end(),R[i])-vec.begin();
            add_edge(l+1,r+1,1,vec[l]-vec[r]);
        }
        mincostmaxflow();
        printf("%d
    ",-cost);
        return 0;
    }
    
  • 相关阅读:
    sqlserver中死锁问题
    sqlserver循环
    自动装箱和拆箱的原理
    资源文件
    SqlServer函数
    PGSql
    SOAP和REST
    Replication
    office等资料下载
    mysql
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11291378.html
Copyright © 2020-2023  润新知