题目链接:点击查看

题目大意:给出n条开线段,开线段的意思就是端点的两个点属于开区间,不属于线段中,让从中选出数条线段,满足:

  1. 在x轴选取任何一个点,选取线段向x轴映射到该点的次数小于等于k
  2. 所选线段长度之和最大

要求输出最大的长度之和

题目分析:和上一道区间的题目大同小异,只不过有一些小坑需要处理:

  1. 在计算长度的时候会爆int
  2. 特判线段垂直于x轴的情况

先说一下为什么要特判垂直于x轴的情况吧,因为如果直线垂直于x轴的话,那么其映射到x轴上的区间[l,r]的l==r了,这样如果直接建边会生成自环,从而spfa跑不出来,所以要特判一下

本来以为处理了这些细节就可以白嫖一个题了,但事与愿违,就是上面的第二个情况最终还是把我弄晕了,因为知道了相交的话肯定是要拆点,但感觉很合理的拆点却无法通过测试点2,最后看了题解的拆点,想了好久才懵懵懂懂的可以接受,就是关于拆点后,必须严格满足:

  1. 可以特判垂直的情况
  2. 原本意义不变

先说一下该怎么拆点吧,对于一组开区间[l,r],让l和r分别扩大两倍:

  1. 如果l==r:令r++
  2. 如果l!=r:令l++

肯定会有人问了,l==r的时候让r++这个可以理解,但l!=r的时候为什么还要让l++呢,其实举个例子就能稍微加深理解了,比如下面两个例子:

两个开区间(1,2)和(2,3),经过放缩后变为(2,4)和(4,6),其实乍一看没有什么问题,但如果变成下面的样子:

三个开区间(1,2),(2,2),(2,3),在这个题目里,(2,2)的意义就是点2了,所以这三个区间连接起来就可以表示为(1,3)了,但经过扩大后我们会得到:(2,4),(4,4),(4,6),为了不生成自环,我们会让r++,但如果只是让r++的话,会变成(2,4),(4,5),(4,6),这样的区间很明显不符合题意,于是我们就需要上面的第二个步骤,也就是令l++后,可以得到正确的区间关系了:(3,4),(4,5),(5,6)

通过这样构造拆点后就可以正确的处理上面提到的细节了

代码:

#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=2e3+100;//点const int M=1e4+100;//边struct Edge
{int to,w,cost,next;
}edge[M];int head[N],cnt;vector<int>v;//离散化用 int get_id(int x)
{return lower_bound(v.begin(),v.end(),x)-v.begin();
}struct Line
{int x1,y1,x2,y2;int len;void cal_len(){len=int(sqrt(1LL*(x1-x2)*(x1-x2)+1LL*(y1-y2)*(y1-y2)));}void input(){scanf("%d%d%d%d",&x1,&y1,&x2,&y2);cal_len();if(x1>x2)swap(x1,x2);x1<<=1;x2<<=1;if(x1==x2)x2++;elsex1++;v.push_back(x1);v.push_back(x2);}
}line[510];void addedge(int u,int v,int w,int cost)
{edge[cnt].to=v;edge[cnt].w=w;edge[cnt].cost=cost;edge[cnt].next=head[u];head[u]=cnt++;edge[cnt].to=u;edge[cnt].w=0;edge[cnt].cost=-cost;edge[cnt].next=head[v];head[v]=cnt++;
}int d[N],incf[N],pre[N];bool vis[N];bool spfa(int s,int t)
{memset(d,0xcf,sizeof(d));memset(vis,false,sizeof(vis));memset(pre,-1,sizeof(pre));queue<int>q;q.push(s);vis[s]=true;incf[s]=inf;d[s]=0;while(!q.empty()){int u=q.front();q.pop();vis[u]=false;for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;int w=edge[i].w;int cost=edge[i].cost;if(!w)continue;if(d[v]<d[u]+cost){d[v]=d[u]+cost;pre[v]=i;incf[v]=min(incf[u],w);if(!vis[v]){vis[v]=true;q.push(v);}}}}return pre[t]!=-1;
}int update(int s,int t)
{int x=t;while(x!=s){int i=pre[x];edge[i].w-=incf[t];edge[i^1].w+=incf[t];x=edge[i^1].to;}return d[t]*incf[t];
}void init()
{memset(head,-1,sizeof(head));cnt=0;
}int solve(int st,int ed)
{int ans=0;while(spfa(st,ed))ans+=update(st,ed);return ans;
}int main()
{
//  freopen("input.txt","r",stdin);
//  ios::sync_with_stdio(false);init();int n,k,st=N-1,ed=st-1;scanf("%d%d",&n,&k);for(int i=1;i<=n;i++)line[i].input();sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());addedge(st,0,k,0);addedge(v.size()-1,ed,k,0);for(int i=1;i<v.size();i++)addedge(i-1,i,inf,0);for(int i=1;i<=n;i++)addedge(get_id(line[i].x1),get_id(line[i].x2),1,line[i].len);printf("%d\n",solve(st,ed));return 0;
}

洛谷 - P3357 最长k可重线段集问题(最大费用最大流+思维建边+拆点)相关推荐

  1. 洛谷 - P3358 最长k可重区间集问题(最大费用最大流+思维建边)

    题目链接:点击查看 题目大意:给出n个开区间,现在要求从中选取一定数量的区间,需要满足: 对于任意点x,所选取的区间中包含点x的个数小于等于k 区间长度和最大 要求输出最长的区间长度和 题目分析:一开 ...

  2. P3357 最长k可重线段集问题(网络流/串联/拆点)

    P3357 最长k可重线段集问题 对于n条开线段,选择一个子集使得任意x=p和子集相交的直线个数小于等于k,并使得选择的线段长度之和最大. 这道题看上去和区间集没有什么区别,只是费用发生变化,但是要注 ...

  3. P3357 最长k可重线段集问题 网络流

    P3357 最长k可重线段集问题 题目描述 给定平面 x-O-yx−O−y 上 nn 个开线段组成的集合 II,和一个正整数 kk .试设计一个算法,从开线段集合 II 中选取出开线段集合 S\sub ...

  4. 洛谷P3357:最长k可重线段集问题(网络流)

    解析 本题的建模方法有很多,我的做法是补集思想转化成志愿者招募然后按照那道题的做法直接做,看题解更多是采用的对于不冲突的线段首尾加边的做法. 在前一道最长k可重区间问题中这两种做法谈不上孰优孰劣,但本 ...

  5. 最长k可重区间集问题最长k可重线段集问题

    题解: 洛谷上这两题的题意都是有问题的 按照标程题意不应该是开区间而是左开右闭区间 然后连边比较巧妙 我们可以看成选k条不相交的路径,其中i-i+1中有k条边 所以建图i-i+1流量为k,权值为0 l ...

  6. 【刷题】LOJ 6014 「网络流 24 题」最长 k 可重区间集

    题目描述 给定实直线 \(L\) 上 \(n\) 个开区间组成的集合 \(I\) ,和一个正整数 \(k\) ,试设计一个算法,从开区间集合 \(I\) 中选取出开区间集合 \(S \subseteq ...

  7. P3358 最长k可重区间集问题(网络流:串联思想)

    P3358 最长k可重区间集问题 这是一个经典模型,给定n个开区间,选择一些区间使得每个位置被覆盖次数不超过k,并最大化选择的区间长度之和. 首先一个直接的想法就是每一个区间匹配了它所对应的点,但是我 ...

  8. 洛谷 - P3980 [NOI2008]志愿者招募(最小费用最大流+思维建边)

    题目链接:点击查看 题目大意:现在有n天需要志愿者,每一天需要招募的人数是Ai个人,现在有m类志愿者,每类志愿者可以在[l,r]天内被招募,需要花费的代价为Ci,现在需要安排一种招募方式,可以使得总花 ...

  9. 网络流二十四题 ————(二十一)、P3358 最长k可重区间集问题 费用流并联与串联选择

    https://www.luogu.com.cn/problem/solution/P3358 洛谷大佬们题解写的很棒,我就不献丑了. 说下我的理解吧,可以把这个限制过程当初物理中的电路并联与串联.流 ...

最新文章

  1. 金融时报:谷歌撤离中国有99.9%的可能性
  2. jquery 读秒,倒数计时方案
  3. ABAP屏幕上显示LIST的三种方法
  4. mysql 并行 更新_MySQL 并行复制(MTS) 从库更新的记录不存在实际却存在
  5. 如何复制mysql数据库_怎么复制mysql数据库到另一台电脑上?
  6. Django操作与内容
  7. 学好Linux决心书
  8. Merkle Patricia Tree (MPT) 以太坊merkle技术分析
  9. 无任何网络提供程序接受指定的网络路径
  10. 小型 web 服务器系统,小型WEB服务器 - 应用服务器是什么_应用服务器有哪些
  11. CSS 动画指南: 原理和实战 (一)
  12. python破解b站验证码实现登陆
  13. 初识AMD型号CPU
  14. 0024-华为OD机考:身高--体重排序
  15. BIGEMAP APP导入/导出文件\照片(kml\shp\cad(dxf)\txt\excel)
  16. CC2530基础实验:(1)按键控制LED跑马灯
  17. 2020-mac教程-sudo spctl --master-disable用不了
  18. iphone功率测试软件,iPhone充电功率检测方法
  19. rand()函数用法
  20. k8s中pod删除不了的原因和正确的删除方法

热门文章

  1. SpringCloud环境搭建
  2. Nginx使用服务信号升级
  3. Spring 工厂的相关的方法
  4. ThemeResolver
  5. 反射获取有参数的成员方法并运行
  6. 反射获取有参数的构造方法并运行
  7. 日志规范之为什么要使用SLF4J
  8. 图片存储解决方案的分析
  9. ReactJS入门之声明周期
  10. 字符串-定义和基本使用