最长k可重区间集问题

最长k可重区间集问题

题目链接 https://www.luogu.org/problemnew/show/3358

做法

所有点向下一个点连容量为k费用为0的边

l和r连容量为1费用为区间长度的边

然后跑最大流最大费用流

(最大费用就是把边权取相反数跑最小费用

最后再输出最终费用的相反数)

思考

在整张图中,只有l - >r的边有费用

而且费用为区间长度

(i->i+1费用为0)

所以跑最大费用也就是求最长区间

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <vector>
    #include <queue>
    #define ul unsigned long long
    #define rg register int
    #define ll long long
    #define il inline
    #define INF 2147483647
    #define SZ 10000000
    using namespace std;
    int n,N,k,s,t,a[SZ],l[SZ],r[SZ];
/* N : 原数组大小

n : 离散化之后的数组大小

a[] : 离散数组

k : 可重迭数

l , r 所给区间左端点和右端点

*/

    struct Edge{int to,nxt,w,c;}e[SZ];
    int Ehead[SZ],pv[SZ],pe[SZ],Ecnt=2;
    il void Eadd(int u,int v,int w,int cost)
    {
        e[Ecnt]=(Edge){v,Ehead[u],w,cost};
        Ehead[u]=Ecnt++;
        e[Ecnt]=(Edge){u,Ehead[v],0,-cost};
        Ehead[v]=Ecnt++;
    }
/* 加边函数

pv[i] : spfa时使得i点dis值松弛的节点

(最短路的上一节点)

pe[i] : i与pv[i]连接的边

e[i].w : 流量

e[i].c : 费用

*/

    // 费用流板子 ‘_‘↓↓↓
    ll dis[SZ];
    int vis[SZ];
    queue <int> Q;
    bool spfa()
    {
        memset(dis,63,sizeof(dis));
        dis[s]=0; Q.push(s);
        while(!Q.empty())
        {
            rg u=Q.front();
            Q.pop();
            for(rg i=Ehead[u];i;i=e[i].nxt)
            {
                rg v=e[i].to;
                if((e[i].w)&&(dis[v]>dis[u]+e[i].c))
                {
                    dis[v]=dis[u]+e[i].c;
                    pe[v]=i;
                    pv[v]=u;
                    if(!vis[v])
                    {
                        vis[v]=1;
                        Q.push(v);
                    }
                }
            }
            vis[u]=0;
        }
        return dis[t]<dis[0];
    }
    il void costflow()
    {
        ll Ans=0;
        while(spfa())
        {
            rg di=INF;
            for(rg i=t;i!=s;i=pv[i])
             di=min(di,e[pe[i]].w);
            for(rg i=t;i!=s;i=pv[i])
            {
                e[pe[i]].w-=di;
                e[pe[i]^1].w+=di;
                Ans+=1ll*di*e[pe[i]].c;
            }
        }
        printf("%lld",-Ans);
    }
    // 费用流板子 ‘_‘↑↑↑
    int main()
    {
        scanf("%d%d",&N,&k);
        for(rg i=1;i<=N;++i)
        {
            scanf("%d%d",&l[i],&r[i]);
            if(l[i]>r[i]) swap(l[i],r[i]);
            a[i]=l[i];a[i+N]=r[i];
        }
        sort(a+1,a+N+N+1);
        n=unique(a+1,a+N+N+1)-a-1;
        for(rg i=1;i<=N;++i)
        {
            rg L=lower_bound(a+1,a+n+1,l[i])-a;
            rg R=lower_bound(a+1,a+n+1,r[i])-a;
            Eadd(L,R,1,l[i]-r[i]);
        }
/* 利用unique和lower_bound离散化

原理是把输入到l[i]与r[i]出现的所有数字

排完序后利用unique去重

注意那些+1-1什么的

*/

        for(rg i=1;i<n;++i)
         Eadd(i,i+1,INF,0);
        s=n+1;t=n+2;
        Eadd(s,1,k,0);
        Eadd(n,t,k,0);
        costflow();
        while(1);
        return 0;
}

原文地址:https://www.cnblogs.com/tply/p/8185899.html

时间: 2024-10-08 11:33:29

最长k可重区间集问题的相关文章

最长k可重区间集(cogs 743)

?问题描述:?编程任务:对于给定的开区间集合I和正整数k,计算开区间集合I的最长k可重区间集的长度.?数据输入:由文件interv.in提供输入数据.文件的第1 行有2 个正整数n和k,分别表示开区间的个数和开区间的可重迭数.接下来的n行,每行有2个整数,表示开区间的两个端点坐标.?结果输出:程序运行结束时,将计算出的最长k可重区间集的长度输出到文件interv.out中.输入文件示例 输出文件示例interv.in4 21 76 87 10 9 13 interv.out 15 /* 朴素的做

[网络流 24 题]最长k可重区间集(费用流)

Description 给定实直线L 上n 个开区间组成的集合I,和一个正整数k,试设计一个算法,从开区间集合I 中选取出开区间集合S属于I,使得在实直线L 的任何一点x,S 中包含点x 的开区间个数不超过k,且sum(|z|)z属于S,达到最大.这样的集合S称为开区间集合I的最长k可重区间集.sum(|z|) z属于S称为最长k可重区间集的长度.对于给定的开区间集合I和正整数k,计算开区间集合I的最长k可重区间集的长度. Solution 1.离散化 然后从每个点i向i+1连一条流量为INF,

【网络流24题】No.21 (最长 k 可重区间集问题 最长不相交路径 最大费用流)

[] 输入文件示例input.txt4 21 76 87 109 13 输出文件示例output.txt15 [分析] 直接co题解好了,写得挺全.. [建模方法] 方法1 按左端点排序所有区间,把每个区间拆分看做两个顶点<i.a><i.b>,建立附加源S汇T,以及附加顶点S'. 1.连接S到S'一条容量为K,费用为0的有向边.2.从S'到每个<i.a>连接一条容量为1,费用为0的有向边.3.从每个<i.b>到T连接一条容量为1,费用为0的有向边.4.从每个

「网络流24题」「LuoguP3358」 最长k可重区间集问题

题目描述 对于给定的开区间集合 I 和正整数 k,计算开区间集合 I 的最长 k可重区间集的长度. 输入输出格式 输入格式: 的第 1 行有 2 个正整数 n和 k,分别表示开区间的个数和开区间的可重迭数.接下来的 n行,每行有 2 个整数,表示开区间的左右端点坐标. 输出格式: 将计算出的最长 k可重区间集的长度输出 输入输出样例 输入样例#1: 复制 4 2 1 7 6 8 7 10 9 13 输出样例#1: 复制 15 说明 对于100%的数据,1<=n<=500,1<=k<

网络流 P3358 最长k可重区间集问题

P3358 最长k可重区间集问题 题目描述 对于给定的开区间集合 I 和正整数 k,计算开区间集合 I 的最长 k可重区间集的长度. 输入输出格式 输入格式: 的第 1 行有 2 个正整数 n和 k,分别表示开区间的个数和开区间的可重迭数.接下来的 n行,每行有 2 个整数,表示开区间的左右端点坐标. 输出格式: 将计算出的最长 k可重区间集的长度输出 输入输出样例 输入样例#1: 复制 4 2 1 7 6 8 7 10 9 13 输出样例#1: 复制 15 说明 对于100%的数据,1\le

「网络流 24 题」最长 k 可重区间集

给定区间集合$I$和正整数$k$, 计算$I$的最长$k$可重区间集的长度. 区间离散化到$[1,2n]$, $S$与$1$连边$(k,0)$, $i$与$i+1$连边$(k,0)$, $2n$与$T$连边$(k,0)$. 对于每个区间$(l,r)$, $l$与$r$连边$(1,l-r)$. 最小费用相反数就为最大长度 #include <iostream> #include <sstream> #include <algorithm> #include <cst

P3358 最长k可重区间集问题

P3358 最长k可重区间集问题 题目链接 #include <bits/stdc++.h> using namespace std; typedef pair<int, int> P; struct edge { int to, cap, cost, rev; edge(int a, int b, int c, int d) : to(a), cap(b), cost(c), rev(d) {} }; const int maxn = 510; const int maxv =

洛谷 P3357 最长k可重线段集问题【最大流】

pre:http://www.cnblogs.com/lokiii/p/8435499.html 和最长k可重区间集问题差不多,也就是价值的计算方法不一样,但是注意这里可能会有x0==x1的情况也就是l==r的情况,然后就TTTTTLE. 其实处理方法很粗暴,因为是开线段,所以可以把它扩大一倍,然后就可以取精度差,对于l!=r,l++,否则l--. 然后正常建模即可. 这个建模大概是用了取补集的思想,把覆盖和没覆盖相转化. #include<iostream> #include<cstd

最长k可重线段集问题

最长k可重线段集问题 时空限制1000ms / 128MB 题目描述 给定平面 x−O−y 上 n 个开线段组成的集合 I,和一个正整数 k .试设计一个算法,从开线段集合 I 中选取出开线段集合 S⊆I ,使得在 x 轴上的任何一点 p,S 中与直线 x=p 相交的开线段个数不超过 k,且∑?∣z∣达到最大.这样的集合 S 称为开线段集合 I 的最长 k 可重线段集.∑?∣z∣ 称为最长 k 可重线段集的长度. 对于任何开线段 z,设其断点坐标为 (x0?,y0?) 和 (x1?,y1?),则