题目大意:

关键词:最小费用最大流 不相交路径

如果两个线段重叠了,那我们则把一个线段放在下一层,另一个线段放在上一层。把流量为1的流看作一条线,一条线把位于同一层的线段(互不重叠)都串了起来。最多有k层,则总流量最多为k。问题变成了:每条线如何串串出的线段的长度总和最大?

构图思路1:同一层每一线段都有可能是该层的第一个线段或是该层的最后一个线段。每个位于该线段后面的、不与该线段重合的线段,都是该线段潜在的同一层的下一个线段。因此连接s与每个区间的左端点、每个区间的右端点和T,每个区间的右端点连接每一个不与该线段重合的线段的左端点。为保证总流量最多为k,S向s连容量为k的边。每个线段费用为-线段长度。其余所有边费用0,容量1。

构图思路2:想象有一条竖直的线从左往右扫,穿线段的线也持续从左往右延伸,则竖直线割到的穿线段的线的数量就等于总流量。我们可以在下面再放一层“高速公路”,每一条线如果能在上面几层串线段就在上面几层穿线段,否则就放在“高速公路的一排车道”上往右延伸。具体做法:S点在最前,T点在最后,每个L上的点向其右面的相邻点连一条容量k费用0的边,两个点如果是线段的端点,则连一条容量1,费用-长度的边。

思路2代码:

#include <cstdio>
#include <cstring>
#include <set>
#include <queue>
#include <cmath>
#include <map>
#include <algorithm>
#include <cassert>
using namespace std; #define LOOP(i,n) for(int i=1; i<=n; i++)
const int MAX_RANGE=, MAX_NODE = MAX_RANGE*, MAX_EDGE = MAX_NODE * MAX_NODE, INF = 0x3f3f3f3f; struct MCMF
{
struct Node;
struct Edge; struct Node
{
Edge *Head, *Prev;
int Dist, Id;
bool Inq;
}; struct Edge
{
int Cap, Cost, OrgCap;
Node *From, *To;
Edge *Next, *Rev;
Edge(int cap, int cost, Node *from, Node *to, Edge *next) :Cap(cap), OrgCap(cap), Cost(cost), From(from), To(to), Next(next) {}
}; Node _nodes[MAX_NODE];
Edge *_edges[MAX_EDGE];
int _vCount, _eCount;
Node *Start, *Sink;
int TotFlow, TotCost; void Init(int n, int sId, int tId)
{
_vCount = n;
_eCount = ;
Start = &_nodes[sId], Sink = &_nodes[tId];
TotFlow = TotCost = ;
} Edge* AddEdge(Node *from, Node *to, int cap, int cost)
{
Edge *e = _edges[++_eCount] = new Edge(cap, cost, from, to, from->Head);
e->From->Head = e;
return e;
} void Build(int uId, int vId, int cap, int cost)
{
Node *u = uId + _nodes, *v = vId + _nodes;
u->Id = uId;
v->Id = vId;
Edge *edge1 = AddEdge(u, v, cap, cost), *edge2 = AddEdge(v, u, , -cost);
edge1->Rev = edge2;
edge2->Rev = edge1;
} bool SPFA()
{
queue<Node*> q;
LOOP(i, _vCount)
{
_nodes[i].Prev = NULL;
_nodes[i].Dist = INF;
_nodes[i].Inq = false;
}
Start->Dist = ;
q.push(Start);
while (!q.empty())
{
Node *u = q.front();
q.pop();
u->Inq = false;
for (Edge *e = u->Head; e; e = e->Next)
{
if (e->Cap && u->Dist + e->Cost < e->To->Dist)
{
e->To->Dist = u->Dist + e->Cost;
e->To->Prev = e;
if (!e->To->Inq)
{
e->To->Inq = true;
q.push(e->To);
}
}
}
}
return Sink->Prev;
} void Proceed()
{
while (SPFA())
{
assert(Sink->Dist != INF);
int minFlow = INF;
for (Edge *e = Sink->Prev; e; e = e->From->Prev)
minFlow = min(minFlow, e->Cap);
TotFlow += minFlow;
for (Edge *e = Sink->Prev; e; e = e->From->Prev)
{
e->Cap -= minFlow;
e->Rev->Cap += minFlow;
TotCost += minFlow * e->Cost;
}
}
}
}g; int main()
{
#ifdef _DEBUG
freopen("c:\\noi\\source\\input.txt", "r", stdin);
#endif
int totRange, totLayer, left[MAX_RANGE], right[MAX_RANGE],
idMatchP[MAX_NODE], sId, tId, id = ;
static map<int, int> pMatchId;
static set<int> pVis;
scanf("%d%d", &totRange, &totLayer);
memset(idMatchP, , sizeof(idMatchP));
memset(left, , sizeof(left));
memset(right, , sizeof(right));
LOOP(i, totRange)
{
scanf("%d%d", &left[i], &right[i]);
if (!pVis.count(left[i]))
{
idMatchP[++id] = left[i];
pVis.insert(left[i]);
}
if (!pVis.count(right[i]))
{
idMatchP[++id] = right[i];
pVis.insert(right[i]);
}
}
sId = id + ;
tId = id + ;
g.Init(tId, sId, tId);
sort(idMatchP + , idMatchP + id + );
LOOP(i, id)
pMatchId.insert(pair<int, int>(idMatchP[i], i));
LOOP(i, totRange)
g.Build(pMatchId[left[i]], pMatchId[right[i]], , left[i] - right[i]);
for (int i = ; i < id; i++)
g.Build(i, i + , totLayer, );
g.Build(sId, , totLayer, );
g.Build(id, tId, totLayer, );
g.Proceed();
printf("%d\n", -g.TotCost);
return ;
}

反思:做网络流的题时,网络流的含义应当清楚。思路多点“广度搜索”。

luogu3358 最长k可重区间集问题 网络流的更多相关文章

  1. COGS743. [网络流24题] 最长k可重区间集

    743. [网络流24题] 最长k可重区间集 ★★★   输入文件:interv.in   输出文件:interv.out   简单对比时间限制:1 s   内存限制:128 MB «问题描述: «编 ...

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

    [] 输入文件示例input.txt4 21 76 87 109 13 输出文件示例output.txt15 [分析] 直接co题解好了,写得挺全.. [建模方法] 方法1 按左端点排序所有区间,把每 ...

  3. (luogu P3358)最长k可重区间集问题 [TPLY]

    最长k可重区间集问题 题目链接 https://www.luogu.org/problemnew/show/3358 做法 所有点向下一个点连容量为k费用为0的边 l和r连容量为1费用为区间长度的边 ...

  4. 【网络流24题】最长k可重区间集(费用流)

    [网络流24题]最长k可重区间集(费用流) 题面 Cogs Loj 洛谷 题解 首先注意一下 这道题目里面 在Cogs上直接做就行了 洛谷和Loj上需要判断数据合法,如果\(l>r\)就要交换\ ...

  5. 【网络流24题21】最长k可重区间集问题

    题面戳我 题目描述 对于给定的开区间集合I和正整数k,计算开区间集合I的最长k可重区间集的长度. 输入格式: 的第 1 行有 2 个正整数n和k,分别表示开区间的个数和开区间的可重迭数.接下来的 n行 ...

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

    P3358 最长k可重区间集问题 题目描述 对于给定的开区间集合 I 和正整数 k,计算开区间集合 I 的最长 k可重区间集的长度. 输入输出格式 输入格式: 的第 1 行有 2 个正整数 n和 k, ...

  7. LibreOJ #6014. 「网络流 24 题」最长 k 可重区间集

    #6014. 「网络流 24 题」最长 k 可重区间集 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据   ...

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

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

  9. [网络流24题] 最长k可重区间集

    https://www.luogu.org/problemnew/show/3358 以区间(1,5),(2,6),(7,8)为例 建模方法一: 建模方法二: 离散化区间端点 相当于找k条费用最大的不 ...

随机推荐

  1. hibernate annotation 生成uuid主键

    JPA标准方式下,不可以生成uuid类型的主键,但是hibernate提供了一些方式生成uuid主键,具体如下: 1.主键生成器     @GeneratedValue(generator=" ...

  2. Linq处理decimal字段汇总Sum()为NULL

    xxxxxxxx.Sum(f => f.jifen).GetValueOrDefault(0)

  3. Idea 创建maven web项目(手工创建)

    参考链接:https://www.cnblogs.com/justuntil/p/7511787.html 话不多说,直接上图: 1.创建maven项目 创建项目完成,项目结构如下: 2.项目部署配置 ...

  4. iptables详解(2):iptables实际操作之规则查询

    所属分类:IPtables  Linux基础 在阅读这篇文章之前,请确保你已经阅读了如下文章,如下文章总结了iptables的相关概念,是阅读这篇文章的基础. 图文并茂理解iptables 如果你是一 ...

  5. python编写webservice接口

    1.pip install suds-jurko 2.pip install client #coding=utf-8 from suds.client import Client class Web ...

  6. Lua练习题集嚢

    --1.table.sort() am = {"cc","nn","ll","dd"} arr = function ( ...

  7. 通过JS唤醒app(安卓+ios)

    有需求说要通过页面按钮唤醒app,或者手机上没有这款app跳转到商店,然后刚开始也是查了资料的,结果发现一头雾水,不过最后还是捣鼓出来了,当然也参考了前人分享的经验,下面我就将方法整理一下: 首先明确 ...

  8. python之BeautifulSoup库

    1. BeautifulSoup库简介 和 lxml 一样,Beautiful Soup 也是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据.lxml 只会局部遍历 ...

  9. php第十八节课

    PDO 对不同的数据库连接使用 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "ht ...

  10. 数据结构与算法(6) -- heap

    binary heap就是一种complete binary tree(完全二叉树).也就是说,整棵binary tree除了最底层的叶节点之外,都是满的.而最底层的叶节点由左至右又不得有空隙. 以上 ...