P3357 最长k可重线段集问题 网络流
P3357 最长k可重线段集问题
题目描述
给定平面 x-O-yx−O−y 上 nn 个开线段组成的集合 II,和一个正整数 kk 。试设计一个算法,从开线段集合 II 中选取出开线段集合 S\subseteq IS⊆I ,使得在 xx 轴上的任何一点 pp,SS 中与直线 x=px=p 相交的开线段个数不超过 kk,且\sum\limits_{z\in S}|z|z∈S∑∣z∣达到最大。这样的集合 SS 称为开线段集合 II 的最长 kk 可重线段集。\sum\limits_{z\in S}|z|z∈S∑∣z∣ 称为最长 kk 可重线段集的长度。
对于任何开线段 zz,设其断点坐标为 (x_0,y_0)(x0,y0) 和 (x_1,y_1)(x1,y1),则开线段 zz 的长度 |z|∣z∣ 定义为:|z|=\lfloor\sqrt{(x_1-x_0)^2+(y_1-y_0)^2}\rfloor∣z∣=⌊(⌋
对于给定的开线段集合 II 和正整数 kk,计算开线段集合 II 的最长 kk 可重线段集的长度。
输入输出格式
输入格式:
文件的第一 行有 22 个正整数 nn 和 kk,分别表示开线段的个数和开线段的可重叠数。
接下来的 nn 行,每行有 44 个整数,表示开线段的 22 个端点坐标。
输出格式:
程序运行结束时,输出计算出的最长 kk 可重线段集的长度。
输入输出样例
说明
1\leq n\leq5001≤n≤500
1 \leq k \leq 131≤k≤13
这个题目和之前的最长k可重区间集问题是一样的,就是把平面上的线段投影到x轴,但是呢,有一个点有问题,就是要
特判两条直线重合且垂直于x轴的这一种情况,具体是为什么呢,我也有点不明白为什么了,好像是会出现环的情况。
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
#include <map>
#include <cstring>
#include <cmath>
#include <string>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5;
struct edge
{
int u, v, c, f;
ll cost;
edge(int u, int v, int c, int f, ll cost) :u(u), v(v), c(c), f(f), cost(cost) {}
};
vector<edge>e;
vector<int>G[maxn];
int a[maxn];//找增广路每个点的水流量
int p[maxn];//每次找增广路反向记录路径
int d[maxn];//SPFA算法的最短路
int inq[maxn];//SPFA算法是否在队列中
int s, t;
void init(int n)
{
for (int i = ; i <= n; i++)G[i].clear();
e.clear();
}
void add(int u, int v, int c, ll cost)
{
e.push_back(edge(u, v, c, , cost));
e.push_back(edge(v, u, , , -cost));
int m = e.size();
G[u].push_back(m - );
G[v].push_back(m - );
}
bool bellman(int s, int t, int& flow, long long & cost)
{
memset(d, 0xef, sizeof(d));
memset(inq, , sizeof(inq));
d[s] = ; inq[s] = ;//源点s的距离设为0,标记入队
p[s] = ; a[s] = INF;//源点流量为INF(和之前的最大流算法是一样的) queue<int>q;//Bellman算法和增广路算法同步进行,沿着最短路拓展增广路,得出的解一定是最小费用最大流
q.push(s);
while (!q.empty())
{
int u = q.front();
q.pop();
inq[u] = ;//入队列标记删除
for (int i = ; i < G[u].size(); i++)
{
edge & now = e[G[u][i]];
int v = now.v;
if (now.c > now.f && d[v] < d[u] + now.cost)
//now.c > now.f表示这条路还未流满(和最大流一样)
//d[v] > d[u] + e.cost Bellman 算法中边的松弛
{
d[v] = d[u] + now.cost;//Bellman 算法边的松弛
p[v] = G[u][i];//反向记录边的编号
a[v] = min(a[u], now.c - now.f);//到达v点的水量取决于边剩余的容量和u点的水量
if (!inq[v]) { q.push(v); inq[v] = ; }//Bellman 算法入队
}
}
}
if (d[t] < )return false;//找不到增广路
flow += a[t];//最大流的值,此函数引用flow这个值,最后可以直接求出flow
cost += (long long)d[t] * (long long)a[t];//距离乘上到达汇点的流量就是费用
for (int u = t; u != s; u = e[p[u]].u)//逆向存边
{
e[p[u]].f += a[t];//正向边加上流量
e[p[u] ^ ].f -= a[t];//反向边减去流量 (和增广路算法一样)
}
return true;
}
int MaxcostMaxflow(int s, int t, long long & cost)
{
cost = ;
int flow = ;
while (bellman(s, t, flow, cost));//由于Bellman函数用的是引用,所以只要一直调用就可以求出flow和cost
return flow;//返回最大流,cost引用可以直接返回最小费用
} struct node
{
int xx1, yy1, xx2, yy2;
ll cost;
}exa[maxn];
bool cmp(node a, node b)
{
return a.xx1 < b.xx1;
} ll dis(int x, int y, int x1, int y1)
{
return sqrt((x - x1) * 1ll * (x - x1) + (y - y1) * 1ll * (y - y1));
} int main()
{
int n, m;
cin >> n >> m;
int s1 = ;
s = , t = * n + ;
for (int i = ; i <= n; i++)
{
cin >> exa[i].xx1 >> exa[i].yy1 >> exa[i].xx2 >> exa[i].yy2;
if (exa[i].xx1 > exa[i].xx2)
{
swap(exa[i].xx1, exa[i].xx2);
swap(exa[i].yy1, exa[i].yy2);
}
exa[i].cost = dis(exa[i].xx1, exa[i].yy1, exa[i].xx2, exa[i].yy2);
}
sort(exa + , exa + + n, cmp);
add(s, s1, m, );
for (int i = ; i <= n; i++)
{
add(s1, + * i - , , );
add( + * i - , + * i, , exa[i].cost);
add( + * i, t, , );
for (int j = ; j < i; j++)
{
if (exa[j].xx2 == exa[i].xx1&&exa[j].xx1 == exa[j].xx2&&exa[i].xx1==exa[i].xx2) continue;
if (exa[j].xx2 <= exa[i].xx1) add( + * j, + * i - , , );
}
}
ll cost = ;
int ans = MaxcostMaxflow(s, t, cost);
printf("%lld\n", cost);
return ;
}
P3357 最长k可重线段集问题 网络流的更多相关文章
- 洛谷P3357 最长k可重线段集问题(费用流)
传送门 其实和最长k可重区间集问题差不多诶…… 把这条开线段给压成x轴上的一条线段,然后按上面说的那种方法做即可 然而有一个坑点是线段可以垂直于x轴,然后一压变成一个点,连上正权环,求最长路……然后s ...
- 洛谷P3357 最长k可重线段集问题(费用流)
题目描述 给定平面 x-O-yx−O−y 上 nn 个开线段组成的集合 II ,和一个正整数 kk .试设计一个算法,从开线段集合 II 中选取出开线段集合 S\subseteq IS⊆I ,使得在 ...
- 洛谷 P3357 最长k可重线段集问题【最大流】
pre:http://www.cnblogs.com/lokiii/p/8435499.html 和最长k可重区间集问题差不多,也就是价值的计算方法不一样,但是注意这里可能会有x0==x1的情况也就是 ...
- luogu P3357 最长k可重线段集问题
这题和3358一模一样,建模形式直接不用变,就两点不一样,一是len变化了,加入y后再更新即可,还有就是可能会出现x0=x1的情况,即一条开线段垂直x轴,如果我们依旧按照上一题的建图方法,就会出现负权 ...
- 网络流24题-最长k可重线段集问题
最长k可重线段集问题 时空限制1000ms / 128MB 题目描述 给定平面 x−O−y 上 n 个开线段组成的集合 I,和一个正整数 k .试设计一个算法,从开线段集合 I 中选取出开线段集合 S ...
- 【网络流24题】最长k可重线段集(费用流)
[网络流24题]最长k可重线段集(费用流) 题面 Cogs的数据有问题 Loj 洛谷 题解 这道题和最长k可重区间集没有区别 只不过费用额外计算一下 但是,还是有一点要注意的地方 这里可以是一条垂直的 ...
- 【刷题】LOJ 6227 「网络流 24 题」最长k可重线段集问题
题目描述 给定平面 \(\text{xoy}\) 上 \(n\) 个开线段组成的集合 \(\text{I}\) ,和一个正整数 \(k\) ,试设计一个算法. 从开线段集合 \(\text{I}\) ...
- [网络流24题]最长k可重线段集[题解]
最长 \(k\) 可重线段集 题目大意 给定平面 \(x-O-y\) 上 \(n\) 个开线段组成的集合 \(I\) ,和一个正整数 \(k\) .试设计一个算法,从开线段集合 \(I\) 中选取开线 ...
- 【网络流24题22】最长k可重线段集问题
题面戳我 sol 千万!千万!不要理解错题意了!最长K可重,不是说线段最多K可重!你以为计算几何? 原文:使得在\(x\)轴上的任何一点\(p\),\(S\)中与直线\(x=p\)相交的开线段个数不超 ...
随机推荐
- Discuz 任务、道具、任务和验证类插件制作
一.广告类脚本位置:source/class/adv/adv_name.php语言包位置:source/language/adv/lang_name.php <?php class adv_na ...
- django一对一数据库建立和进行数据传输的3种方式all()(对象) values()(字典) values_list()(元组)
class Business(models.Model): caption = models.CharField(max_length=32) code = models.CharField(max_ ...
- git_基本使用
1.默认你已经安装了,git的客户端,这里我们使用git bash操作. 2.执行git init命令: git ini 3.在本地创建ssh key: ssh-keygen -t rsa - ...
- python变量作用域
[python变量作用域] 几个概念: python能够改变变量作用域的代码段是def.class.lamda. if/elif/else.try/except/finally.for/while 并 ...
- Excel VBA入门(九)操作工作薄
虽然我前面讲过,在VBA中操作工作薄并不是件明智的事,但有些时候,还是避免不了要这么做.绝大多数情况下,我们要做的是获取到某个工作薄对象,并以此来获得其中的工作表对象,然后再对工作表中的数据进行处理. ...
- JAVA获取时间的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ...
- Spring boot 、swagger、c3p0、mybatis和redis 整合
文件路径 添加依赖 <?xml version="1.0" encoding="UTF-8"?> <projec ...
- jdeveloper 恢复默认配置
1>jdeveloper的环境设置出现问题,恢复默认的配置,需要删除保存再登录账户中的配置文件,以达到恢复默认配置的目的.只需删除以下配置文件目录即可. C:\Users\当前登录用户名\App ...
- python pip 代理设置
pip install --proxy="user:password@server:port" packagename origin url: http://xiuxixiuxi. ...
- CSS中float和Clear的使用
CSS中float和Clear的使用 本文和大家重点讨论一下CSS中Float和Clear属性的使用,一个float对象可以居左或居右,一个设置为float的对象,将根据设置的方向,左移或右移到其父容 ...