P1324 矩形分割(贪心)

在这里插入图片描述

贪心题目:

思路:先切代价大的。

如果先切代价小的,那么另一个方向的相对代价较大的会切更多次,所以要先切代价大的。

代码细节:
总共切(n + m - 2)次

那么对横向和纵向的代价排序,切较大的即可。

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 2020;

int n , m , row , col;
int a[N] , b[N];

bool cmp(int x , int y)
{
    return x > y;
}

int main()
{
    cin >> n >> m;
    for(int i = 1; i < n; i ++) cin >> a[i];
    for(int i = 1; i < m; i ++) cin >> b[i];
    
    row = 1 , col = 1; // 表示横着的块数和竖着的块数,也表示代价的下标
    
    sort(a + 1 , a + n , cmp);
    sort(b + 1 , b + m , cmp);
    
    long long res = 0;
    
    for(int i = 2; i < n + m; i ++)   
    {
        if(a[row] > b[col]) res += a[row ++] * col;
        else res += row * b[col ++];
    }
    cout << res << endl;
    return 0;
}
<think>嗯,用户想用C#实现计算平面内所有不相交线段的算法,这属于计算几何的范畴。首先,我得回忆一下相关的算法。记得线段相交检测通常需要用到向量叉积来判断线段是否相交。用户可能需要先处理大量的线段,找出其中互不相交的集合。 首先,基础部分应该是如何判断两线段是否相交。根据引用[3]提到的线-线相交算法,可能需要使用快速排斥实验和跨立实验。这部分需要正确实现数学判断,比如使用参数方程或者向量叉积的方法。比如,计算两条线段的方向向量,然后用叉积判断它们是否跨立。 接下来,用户可能需要遍历所有线段对,检测它们是否相交。但这种方法的时间复杂度是O(),对于大量线段来说效率很低。所以需要考虑优化方法。引用[1]提到,尝试所有顶点对的组合,并结合分区来加快算法。可能这里可以引入空间分割或者扫描线算法,比如Bentley-Ottmann算法,但实现起来比较复杂。 然后,用户的问题是要找出所有不相交的线段,可能是指从给定线段集合中筛选出与其他线段都不相交的那些。或者可能是将线段划分成多个互不相交的组?需要明确用户的需求。假设是前者,即找出所有与其他线段都不相交的线段,那么算法需要遍历每条线段,检查它是否与集合中的其他线段相交。但如果是后者,可能需要更复杂的处理方法,比如平面分割或者使用图论中的独立集概念,但这样计算量会很大。 考虑到用户可能的应用场景,比如CAD绘图或者游戏开发中的碰撞检测,需要高效的方法。引用[2]提到Wykobi库中的计算几何算法,可能有一些优化的数据结构,比如扫描线算法或者空间索引(如四叉树)来减少需要检测的线段对数量。 在C#中实现的话,可能需要先定义线段的结构,包含起点和终点。然后实现线段相交的判断函数。接着,使用双重循环遍历所有线段对,但这样效率不高。为了优化,可以考虑空间分区,比如将平面划分为网格,每个网格中的线段只需与相邻网格中的线段进行检测,减少计算量。 另外,引用[1]还提到选择通过顶点的线段,可能涉及到极角排序或者凸包算法,但不确定是否相关。需要根据用户的具体问题来决定。例如,如果用户是要找到最大的不相交线段子集,这可能需要贪心算法或者动态规划,但这类问题通常是NP难的,可能需要近似解法。 现在,用户的具体需求是“平面内所有不相交线段”,可能有两种情况:1)所有线段本身互不相交,需要验证;2)从给定线段集合中找出所有不与其他线段相交的线段。如果是情况2,那么算法需要遍历每条线段,检查它是否与其他任何线段相交。这时候,双重循环的时间复杂度是O(),可能需要优化。 所以,初步的步骤应该是: 1. 实现线段相交检测函数。 2. 遍历所有线段,对于每条线段,检查是否与其他线段相交。 3. 收集所有不与其他线段相交的线段。 为了提高效率,可以引入空间索引,比如四叉树或网格划分,将线段分配到不同的区域,只检查可能相交的相邻区域中的线段,减少比较次数。 在C#中,可能需要使用结构体或类来表示线段,处理浮点数精度问题,比如使用double类型,并在比较时考虑误差范围。例如,判断点是否在线段上时,使用极小的epsilon值来避免浮点误差。 另外,引用[4]提到了多边形和平行切线,可能涉及面积函数和斜率,这可能与线段的方向有关,但不确定是否直接相关。可能需要进一步分析,但暂时可以专注于线段相交的基本算法。 总结,用户的实现步骤可能包括: - 数据结构定义(线段结构) - 相交检测函数 - 遍历优化(空间分区) - 收集结果 需要注意的边界情况包括:线段端点重合、线段部分重叠、平行线段等。这些情况在相交检测中需要正确处理,避免误判。 最后,生成相关问题时,需要考虑用户可能进一步的需求,如性能优化、处理大量线段的方法、特定应用场景下的算法变种等。</think>### 分步实现平面内不相交线段的检测算法 #### 1. 线段相交判断基础 线段相交检测需要两个几何判断条件: - **快速排斥实验**:判断两线段的外接矩形是否相交 - **跨立实验**:使用向量叉积判断线段方向关系 数学表达式: 给定线段$p_1p_2$和$p_3p_4$,向量叉积计算为: $$ \begin{aligned} v_1 &= (p_4 - p_3) \times (p_1 - p_3) \\ v_2 &= (p_4 - p_3) \times (p_2 - p_3) \\ v_3 &= (p_2 - p_1) \times (p_3 - p_1) \\ v_4 &= (p_2 - p_1) \times (p_4 - p_1) \end{aligned} $$ 当$v_1 \cdot v_2 < 0$且$v_3 \cdot v_4 < 0$时线段相交[^3] #### 2. C#核心实现代码 ```csharp public struct Point { public double X, Y; } public struct Segment { public Point Start, End; } public class SegmentIntersectionChecker { // 叉积计算 private static double Cross(Point a, Point b, Point c) => (b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X); // 快速排斥实验 private static bool BoundingBoxCheck(Segment a, Segment b) => Math.Max(a.Start.X, a.End.X) >= Math.Min(b.Start.X, b.End.X) && Math.Max(b.Start.X, b.End.X) >= Math.Min(a.Start.X, a.End.X) && Math.Max(a.Start.Y, a.End.Y) >= Math.Min(b.Start.Y, b.End.Y) && Math.Max(b.Start.Y, b.End.Y) >= Math.Min(a.Start.Y, a.End.Y); // 完整相交判断 public static bool SegmentsIntersect(Segment seg1, Segment seg2) { if (!BoundingBoxCheck(seg1, seg2)) return false; double c1 = Cross(seg1.Start, seg1.End, seg2.Start); double c2 = Cross(seg1.Start, seg1.End, seg2.End); double c3 = Cross(seg2.Start, seg2.End, seg1.Start); double c4 = Cross(seg2.Start, seg2.End, seg1.End); if (c1 * c2 < 0 && c3 * c4 < 0) return true; // 处理端点重合的特殊情况 const double epsilon = 1e-10; Func<Point, Segment, bool> onSegment = (p, s) => Math.Abs((s.End.X - s.Start.X) * (p.Y - s.Start.Y) - (p.X - s.Start.X) * (s.End.Y - s.Start.Y)) < epsilon; return (Math.Abs(c1) < epsilon && onSegment(seg2.Start, seg1)) || (Math.Abs(c2) < epsilon && onSegment(seg2.End, seg1)) || (Math.Abs(c3) < epsilon && onSegment(seg1.Start, seg2)) || (Math.Abs(c4) < epsilon && onSegment(seg1.End, seg2)); } } ``` #### 3. 算法优化策略 - **空间分区优化**:将平面划分为网格,只检测相邻网格中的线段[^1] ```csharp public class SpatialPartition { private readonly Dictionary<(int, int), List<Segment>> grid = new(); private readonly double cellSize; public SpatialPartition(double cellSize = 100.0) => this.cellSize = cellSize; private (int, int) GetCell(Point p) => ((int)(p.X / cellSize), (int)(p.Y / cellSize)); public void AddSegment(Segment seg) { var cells = new HashSet<(int, int)>(); // 获取线段覆盖的所有网格单元(需实现遍历算法) // ... foreach (var cell in cells) grid[cell].Add(seg); } public List<Segment> GetCandidateSegments(Segment seg) { var candidates = new List<Segment>(); // 获取相邻网格中的线段 // ... return candidates.Distinct().ToList(); } } ``` #### 4. 完整处理流程 ```csharp public List<Segment> FindNonIntersectingSegments(List<Segment> allSegments) { var result = new List<Segment>(); var spatial = new SpatialPartition(); foreach (var seg in allSegments) spatial.AddSegment(seg); foreach (var current in allSegments) { bool hasIntersection = false; var candidates = spatial.GetCandidateSegments(current); foreach (var other in candidates) { if (current.Equals(other)) continue; if (SegmentIntersectionChecker.SegmentsIntersect(current, other)) { hasIntersection = true; break; } } if (!hasIntersection) result.Add(current); } return result; } ``` ### 注意事项 1. 浮点精度问题:建议使用`double`类型并设置误差范围`epsilon` 2. 特殊边界情况处理:包括端点重合、线段重叠等 3. 性能优化:当线段数量超过$10^4$时,建议使用扫描线算法(Bentley-Ottmann算法)可将复杂度降至$O((n+k)\log n)$,其中k是交点数量[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

11来了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值