杭电 1558 Segment set(并查集+线段相交)

本文介绍了一个关于线段集的问题,通过使用并查集的方法来解决寻找特定线段集大小的问题。具体包括输入命令格式、如何判断线段是否相交以及如何合并线段集等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Segment set

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3451    Accepted Submission(s): 1284


Problem Description
A segment and all segments which are connected with it compose a segment set. The size of a segment set is the number of segments in it. The problem is to find the size of some segment set.

 

Input
In the first line there is an integer t - the number of test case. For each test case in first line there is an integer n (n<=1000) - the number of commands.

There are two different commands described in different format shown below:

P x1 y1 x2 y2 - paint a segment whose coordinates of the two endpoints are (x1,y1),(x2,y2).
Q k - query the size of the segment set which contains the k-th segment.

k is between 1 and the number of segments in the moment. There is no segment in the plane at first, so the first command is always a P-command.
 

Output
For each Q-command, output the answer. There is a blank line between test cases.
 

Sample Input
1 10 P 1.00 1.00 4.00 2.00 P 1.00 -2.00 8.00 4.00 Q 1 P 2.00 3.00 3.00 1.00 Q 1 Q 3 P 1.00 4.00 8.00 2.00 Q 2 P 3.00 3.00 6.00 -2.00 Q 5
 

Sample Output
1 2 2 2 5
 

Author
LL
/*
看的人家的代码后写的,输入P之后添加数据很巧妙,将k初始化成 1 之后再次输入后与前边小于 k 的挨着进行判断,之后k++,再输入下一组数据
关键点:快速跨立实验判断线段相交
Time:2014-08-22 21:42
总在努力比别人投入更多时间,但却总是不如别人,或许,天赋的确很重要,但我仍需要前进,笨笨就笨笨吧,无所谓别人怎么看,努力去做自己。加油!!!
*/
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
const int MAX=1010;
struct Point{
	double x,y; 
} s[MAX],e[MAX];
int father[MAX],numSon[MAX];
int findRoot(int x){
return father[x]==x?father[x]:father[x]=findRoot(father[x]);
}
double mul(Point a,Point c,Point b){
	return (c.x-a.x)*(b.y-a.y)-(b.x-a.x)*(c.y-a.y);
}
bool Intersect(Point a,Point b,Point c,Point d){//快速跨立排斥实验
	if(min(a.x,b.x)>max(c.x,d.x)) return false;
	if(min(a.y,b.y)>max(c.y,d.y)) return false;
	if(max(a.x,b.x)<min(c.x,d.x)) return false;
	if(max(a.y,b.y)<min(c.y,d.y)) return false;
	if(mul(a,c,b)*mul(a,b,d)<0) return false;
	if(mul(c,a,d)*mul(c,d,b)<0) return false;
	return true;
}
void merge(int a,int b){//并查集
	a=findRoot(a);
	b=findRoot(b);
	if(a!=b){
		father[b]=a;
		numSon[a]+=numSon[b];
		numSon[b]=0;
	}
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		int N;
		scanf("%d",&N);
		for(int i=0;i<=N;i++){
			father[i]=i;
			numSon[i]=1;
		}
		char cmd[3];
			int k=1;
		while(N--){
			scanf("%s",cmd);
			if(cmd[0]=='P'){
				scanf("%lf%lf%lf%lf",&s[k].x,&s[k].y,&e[k].x,&e[k].y);
				for(int i=1;i<k;i++){
					if(Intersect(s[i],e[i],s[k],e[k])){
						merge(i,k);
					}
				}
				k++;
			}else{
				int pos;
				scanf("%d",&pos);
				printf("%d\n",numSon[findRoot(pos)]);
			}
		}
		if(T)printf("\n");
	}
return 0;
} 

using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; using System; using System.Collections.Generic; using System.Linq; namespace OptimizedMergePlugin { public class Commands { [CommandMethod("MG", CommandFlags.Modal)] public void MergeLines() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; try { // 获取选择集 PromptSelectionResult selResult = ed.GetSelection(); if (selResult.Status != PromptStatus.OK) return; using (Transaction tr = db.TransactionManager.StartTransaction()) { // 按图层分组并收集所有线段 var layerGroups = new Dictionary<string, List<LineSegment>>(); CollectLineSegments(selResult, tr, layerGroups, ed); // 处理每个图层 BlockTableRecord btr = (BlockTableRecord)tr.GetObject( db.CurrentSpaceId, OpenMode.ForWrite); int totalLayers = layerGroups.Count; int currentLayer = 0; foreach (var kvp in layerGroups) { currentLayer++; ed.WriteMessage($"\n处理图层: {kvp.Key} ({currentLayer}/{totalLayers})"); // 使用空间索引加速处理 var spatialIndex = BuildSpatialIndex(kvp.Value); // 打断重叠线段 List<LineSegment> brokenSegments = BreakSegmentsWithIndex(kvp.Value, spatialIndex, ed); // 合并共线线段 List<Line> mergedLines = MergeSegments(brokenSegments); // 添加新实体 foreach (Line newLine in mergedLines) { newLine.Layer = kvp.Key; btr.AppendEntity(newLine); tr.AddNewlyCreatedDBObject(newLine, true); } } tr.Commit(); } ed.WriteMessage("\n线段合并完成!"); } catch (System.Exception ex) { ed.WriteMessage($"\n错误: {ex.Message}"); } } // 公开的线段表示类 public class LineSegment { public Point3d Start { get; set; } public Point3d End { get; set; } public ObjectId OriginalId { get; set; } = ObjectId.Null; public LineSegment(Point3d start, Point3d end) { Start = start; End = end; } public Extents3d Extents => new Extents3d(Start, End); } // 收集所有线段 private void CollectLineSegments(PromptSelectionResult selResult, Transaction tr, Dictionary<string, List<LineSegment>> layerGroups, Editor ed) { int totalObjects = selResult.Value.Count; int processed = 0; foreach (SelectedObject selObj in selResult.Value) { processed++; // 每处理100个对象显示一次进度 if (processed % 100 == 0) ed.WriteMessage($"\n已处理: {processed}/{totalObjects}"); Entity ent = tr.GetObject(selObj.ObjectId, OpenMode.ForRead) as Entity; if (ent == null) continue; string layer = ent.Layer; if (!layerGroups.ContainsKey(layer)) layerGroups.Add(layer, new List<LineSegment>()); // 处理直线 if (ent is Line line) { layerGroups[layer].Add(new LineSegment(line.StartPoint, line.EndPoint) { OriginalId = line.ObjectId }); } // 处理多段线 else if (ent is Polyline pline) { for (int i = 0; i < pline.NumberOfVertices - 1; i++) { layerGroups[layer].Add(new LineSegment( pline.GetPoint3dAt(i), pline.GetPoint3dAt(i + 1) )); } } } } // 构建空间索引 private SpatialIndex BuildSpatialIndex(List<LineSegment> segments) { SpatialIndex index = new SpatialIndex(); foreach (LineSegment seg in segments) { index.Add(seg.Extents, seg); } return index; } // 使用空间索引打断线段 private List<LineSegment> BreakSegmentsWithIndex(List<LineSegment> segments, SpatialIndex index, Editor ed) { List<LineSegment> result = new List<LineSegment>(); HashSet<LineSegment> processed = new HashSet<LineSegment>(); int totalSegments = segments.Count; int processedCount = 0; foreach (LineSegment seg in segments) { processedCount++; // 每处理100个线段显示一次进度 if (processedCount % 100 == 0) ed.WriteMessage($"\n打断线段: {processedCount}/{totalSegments}"); if (processed.Contains(seg)) continue; List<LineSegment> currentSegments = new List<LineSegment> { seg }; bool modified; do { modified = false; List<LineSegment> nextSegments = new List<LineSegment>(); foreach (LineSegment current in currentSegments) { // 使用空间索引查找可能相交线段 var candidates = index.Search(current.Extents) .Where(s => !processed.Contains(s) && s != current) .ToList(); bool broken = false; foreach (var candidate in candidates) { if (TryBreakSegment(current, candidate, out List<LineSegment> newSegments)) { // 将打断后的线段加入待处理列表 nextSegments.AddRange(newSegments); processed.Add(candidate); broken = true; break; } } if (!broken) { nextSegments.Add(current); } else { modified = true; } } currentSegments = nextSegments; } while (modified); result.AddRange(currentSegments); processed.Add(seg); } return result; } // 尝试打断两条线段 private bool TryBreakSegment(LineSegment seg1, LineSegment seg2, out List<LineSegment> result) { result = new List<LineSegment>(); Line line1 = new Line(seg1.Start, seg1.End); Line line2 = new Line(seg2.Start, seg2.End); Point3dCollection intersections = new Point3dCollection(); // 修复:使用 IntPtr.Zero 代替容差指针参数 line1.IntersectWith(line2, Intersect.OnBothOperands, intersections, IntPtr.Zero, IntPtr.Zero); // 关键修改在这里 if (intersections.Count == 0) return false; // 处理交点 List<Point3d> splitPoints = new List<Point3d>(); foreach (Point3d pt in intersections) { splitPoints.Add(pt); } // 按距离起点排序 splitPoints.Sort((a, b) => seg1.Start.DistanceTo(a).CompareTo(seg1.Start.DistanceTo(b))); // 分割第一条线段 Point3d lastPoint = seg1.Start; foreach (Point3d splitPoint in splitPoints) { result.Add(new LineSegment(lastPoint, splitPoint)); lastPoint = splitPoint; } result.Add(new LineSegment(lastPoint, seg1.End)); return true; } // 合并线段 private List<Line> MergeSegments(List<LineSegment> segments) { // 按起点排序 List<LineSegment> sortedSegments = segments .OrderBy(s => s.Start.X) .ThenBy(s => s.Start.Y) .ThenBy(s => s.Start.Z) .ToList(); List<Line> result = new List<Line>(); LineSegment current = null; foreach (LineSegment seg in sortedSegments) { if (current == null) { current = seg; continue; } // 检查是否共线且连接 if (AreSegmentsConnectable(current, seg)) { // 扩展当前线段 current = new LineSegment( PointMin(current.Start, seg.Start), PointMax(current.End, seg.End) ); } else { // 添加完成合并的线段 result.Add(new Line(current.Start, current.End)); current = seg; } } if (current != null) { result.Add(new Line(current.Start, current.End)); } return result; } // 检查线段是否可连接 private bool AreSegmentsConnectable(LineSegment seg1, LineSegment seg2) { Vector3d dir1 = seg1.End - seg1.Start; Vector3d dir2 = seg2.End - seg2.Start; // 使用全局容差检查方向是否平行 bool isColinear = dir1.IsParallelTo(dir2, Tolerance.Global); // 使用全局容差检查端点连接 bool isConnected = seg1.End.IsEqualTo(seg2.Start, Tolerance.Global) || seg1.Start.IsEqualTo(seg2.End, Tolerance.Global) || seg1.End.IsEqualTo(seg2.End, Tolerance.Global) || seg1.Start.IsEqualTo(seg2.Start, Tolerance.Global); return isColinear && isConnected; } // 辅助方法:获取最小点 private Point3d PointMin(Point3d a, Point3d b) { return new Point3d( Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Min(a.Z, b.Z)); } // 辅助方法:获取最大点 private Point3d PointMax(Point3d a, Point3d b) { return new Point3d( Math.Max(a.X, b.X), Math.Max(a.Y, b.Y), Math.Max(a.Z, b.Z)); } } // 空间索引实现(公开类) public class SpatialIndex { private List<Tuple<Extents3d, Commands.LineSegment>> _items = new List<Tuple<Extents3d, Commands.LineSegment>>(); public void Add(Extents3d extents, Commands.LineSegment segment) { _items.Add(new Tuple<Extents3d, Commands.LineSegment>(extents, segment)); } public IEnumerable<Commands.LineSegment> Search(Extents3d searchExtents) { // 简单实现:检查边界框重叠 return _items .Where(item => item.Item1.MinPoint.X <= searchExtents.MaxPoint.X && item.Item1.MaxPoint.X >= searchExtents.MinPoint.X && item.Item1.MinPoint.Y <= searchExtents.MaxPoint.Y && item.Item1.MaxPoint.Y >= searchExtents.MinPoint.Y) .Select(item => item.Item2); } } } 还是删除不掉重叠线
最新发布
07-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值