CGAL内置的边塌陷算法代码解析

2024-01-26 22:44

本文主要是介绍CGAL内置的边塌陷算法代码解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这个algorithm.run()就是实际的边塌陷算法具体实现

int edge_collapse(TM& tmesh,const ShouldStop& should_stop,// optional mesh information policiesconst GT& traits,const VertexIndexMap& vim, // defaults to get(vertex_index, tmesh)const VertexPointMap& vpm, // defaults to get(vertex_point, tmesh)const HalfedgeIndexMap& him, // defaults to get(edge_index, tmesh)const EdgeIsConstrainedMap& ecm, // defaults to No_constrained_edge_map<TM>()// optional strategy policies - defaults to LindstomTurkconst GetCost& get_cost,const GetPlacement& get_placement,const ShouldIgnore& should_ignore,Visitor visitor)
{typedef EdgeCollapse<TM, GT, ShouldStop,VertexIndexMap, VertexPointMap, HalfedgeIndexMap, EdgeIsConstrainedMap,GetCost, GetPlacement, ShouldIgnore, Visitor> Algorithm;Algorithm algorithm(tmesh, traits, should_stop, vim, vpm, him, ecm, get_cost, get_placement, should_ignore, visitor);return algorithm.run();
}

run()实现函数,重点关注collect()和loop()

template<class TM, class GT, class SP, class VIM, class VPM,class HIM, class ECM, class CF, class PF, class SI, class V>
int
EdgeCollapse<TM,GT,SP,VIM,VPM,HIM,ECM,CF,PF,SI,V>::
run()
{CGAL_expensive_precondition(is_valid_polygon_mesh(m_tm) && CGAL::is_triangle_mesh(m_tm));m_visitor.OnStarted(m_tm);// this is similar to the visitor, but for the cost/stop/placement oraclesinternal::Oracles_initializer<Self>(*this)();// First collect all candidate edges in a PQcollect();// Then proceed to collapse each edge in turnloop();CGAL_SMS_TRACE(0, "Finished: " << (m_initial_edge_count - m_current_edge_count) << " edges removed.");int r = int(m_initial_edge_count - m_current_edge_count);m_visitor.OnFinished(m_tm);return r;
}

1、数据结构 

它使用C++模板编程来定义多种类型,这些类型用于处理网格数据结构中的不同元素和操作。下面是每个类型定义的解释:

1. **基本网格和几何类型**:
   - `Triangle_mesh`: 表示三角形网格的类型。
   - `Geom_traits`: 几何特征类,包含用于处理网格的几何计算的函数和类型。
   - `Should_ignore`: 决定是否应该忽略某些边的策略类型。
   - `Vertex_index_map`: 顶点索引映射类型,用于访问顶点的索引。
   - `Vertex_point_map`: 顶点点映射类型,用于访问顶点的位置。
   - `Halfedge_index_map`: 半边索引映射类型,用于访问半边的索引。
   - `Edge_is_constrained_map`: 表示边是否受约束的映射类型。
   - `Get_cost`: 计算边塌陷成本的函数类型。
   - `Get_placement`: 在边塌陷时确定新顶点位置的函数类型。
   - `Should_stop`: 决定何时停止边塌陷算法的策略类型。
   - `Visitor`: 用于在算法的不同阶段进行通知和记录的访问者类型。

2. **边塌陷算法的专用类型**:
   - `Profile`: 存储有关特定边的信息(如顶点和相邻面)的类型。
   - `Graph_traits`: 表示网格的图形特征的类型。

3. **图形元素描述符和迭代器**:
   - `vertex_descriptor`: 顶点的描述符。
   - `vertex_iterator`: 遍历顶点的迭代器。
   - `halfedge_descriptor`: 半边的描述符。
   - `halfedge_iterator`: 遍历半边的迭代器。
   - `edge_descriptor`: 边的描述符。
   - `edge_iterator`: 遍历边的迭代器。
   - `out_edge_iterator`: 遍历某一顶点发出的边的迭代器。

4. **其他基本类型**:
   - `size_type`: 表示大小或数量的类型。
   - `Point`: 表示点的类型。
   - `FT`: 几何特征中用于表示数值的类型(通常是浮点类型)。
   - `Vector`: 表示向量的类型。
   - `Equal_3`: 用于比较三维点或向量是否相等的函数类型。

5. **可选类型**:
   - `Cost_type`: 表示边塌陷成本的可选类型。
   - `Placement_type`: 表示边塌陷后新顶点位置的可选类型。

2、collect()

这个collect()方法是CGAL中EdgeCollapse类的一个成员函数,用于收集网格中所有可以进行塌陷操作的边,并将它们添加到优先队列中

  • 方法首先初始化一些局部变量来跟踪边的数量和边数据。
  • 它遍历网格中的所有边,并对每条边创建一个Profile对象。这个Profile对象包含关于边的信息,如它的顶点和相邻面。
  • 对于每条边,如果它不是受约束的边(即可以塌陷的边),它会计算这条边的塌陷成本,并将其添加到优先队列中。
  • 如果边的两个端点位置相同(即边的长度为零),则将这条边加入到一个特殊的集合中以后续处理。
  • 最后,对于长度为零的边,它会执行一些特殊的处理,包括从优先队列中移除相关边,并执行塌陷操作。
template<class TM, class GT, class SP, class VIM, class VPM,class HIM, class ECM, class CF, class PF, class SI, class V>
void
EdgeCollapse<TM,GT,SP,VIM,VPM,HIM,ECM,CF,PF,SI,V>::
collect()
{CGAL_SMS_TRACE(0, "collecting edges...");// loop over all the _undirected_ edges in the surface putting them in the PQ//获取网格m_tm中边的总数,并设置初始边数(m_initial_edge_count)和当前边数(m_current_edge_count)。const size_type ne = num_edges(m_tm); // if the mesh has garbage, you might have "ne > edges(tm).size()"m_initial_edge_count = m_current_edge_count = size_type(edges(m_tm).size());//分配一个新的Edge_data数组,用于存储每条边的数据(如成本)。//初始化优先队列mPQ,该队列将根据成本比较函数Compare_cost对边进行排序。m_edge_data.reset(new Edge_data[ne]);mPQ.reset(new PQ(ne, Compare_cost(this), edge_id(this)));//初始化用于调试的计数器,分别记录插入和未插入优先队列的边的数量。CGAL_assertion_code(size_type num_inserted = 0);CGAL_assertion_code(size_type num_not_inserted = 0);//初始化一个集合,用于存储长度为零的边std::set<halfedge_descriptor> zero_length_edges;//遍历网格中的每一条边。//如果一条边是受约束的(如边界边或不可塌陷的边),则跳过该边并增加未插入计数。for(edge_descriptor e : edges(m_tm)){//e是一个edge_descriptor,它是对应于网格m_tm中一条边的唯一标识符。//halfedge(e, m_tm)函数调用返回与边e关联的一个半边的描述符。const halfedge_descriptor h = halfedge(e, m_tm);if(is_constrained(h)){CGAL_assertion_code(++num_not_inserted);continue; // no not insert constrainted edges}//为每条可处理的边创建一个Profile对象,该对象包含关于边的详细信息。//计算边的塌陷成本,并将其加入优先队列中。//如果边的两端点相同(长度为零),则将其添加到zero_length_edges集合中。const Profile profile = create_profile(h);if(!m_traits.equal_3_object()(profile.p0(), profile.p1())){Edge_data& data = get_data(h);data.cost() = cost(profile);insert_in_PQ(h, data);m_visitor.OnCollected(profile, data.cost());CGAL_assertion_code(++num_inserted);}else{zero_length_edges.insert(primary_edge(h));CGAL_assertion_code(++num_not_inserted);}//调试输出,显示当前处理的边的信息。CGAL_SMS_TRACE(2, edge_to_string(h));}//断言以确保所有的边都被处理(无论是插入还是未插入)。CGAL_assertion(num_inserted + num_not_inserted == m_initial_edge_count);//遍历zero_length_edges集合中的边,并对每条边进行额外处理。for(halfedge_descriptor hd : zero_length_edges){//对于每条长度为零的边,创建Profile对象,并检查其塌陷是否在拓扑上有效。如果不有效,则跳过。const Profile profile = create_profile(hd);if(!is_collapse_topologically_valid(profile))continue;// edges of length 0 removed no longer need to be treatedif(profile.left_face_exists()){halfedge_descriptor h_to_remove = is_constrained(profile.vL_v0()) ?primary_edge(profile.v1_vL()) :primary_edge(profile.vL_v0());zero_length_edges.erase(h_to_remove);Edge_data& data = get_data(h_to_remove);if(data.is_in_PQ()){CGAL_SMS_TRACE(2, "Removing E" << get_edge_id(h_to_remove) << " from PQ");remove_from_PQ(h_to_remove, data);}--m_current_edge_count;}if(profile.right_face_exists()){halfedge_descriptor h_to_remove = is_constrained(profile.vR_v1()) ?primary_edge(profile.v0_vR()) :primary_edge(profile.vR_v1());zero_length_edges.erase(h_to_remove);Edge_data& data = get_data(h_to_remove);if(data.is_in_PQ()){CGAL_SMS_TRACE(2, "Removing E" << get_edge_id(h_to_remove) << " from PQ");remove_from_PQ(h_to_remove, data);}--m_current_edge_count;}--m_current_edge_count;//执行实际的塌陷操作,将顶点折叠到长度为零的边的一个端点上。//更新顶点位置,并调用访问者和Oracle更新器以处理塌陷后的变化。//the placement is trivial, it's always the point itselfPlacement_type placement = profile.p0();vertex_descriptor v = halfedge_collapse_bk_compatibility(profile.v0_v1(), m_ecm);put(m_vpm, v, *placement);m_visitor.OnCollapsed(profile, v);internal::After_collapse_oracles_updater<Self>(*this)(profile, v);}CGAL_SMS_TRACE(0, "Initial edge count: " << m_initial_edge_count);
}

 3、is_collapse_topologically_valid()

这个collect中的方法,用于判断边长为0的边进行塌陷是否满足拓扑结构

  template<class TM, class GT, class SP, class VIM, class VPM, class HIM, class ECM, class CF, class PF, class SI, class V>
boolEdgeCollapse<TM,GT,SP,VIM,VPM,HIM,ECM,CF,PF,SI,V>::
is_collapse_topologically_valid(const Profile& profile)
{//初始化返回值res为true,假设边的塌陷在拓扑上是有效的,直到检测到相反的情况bool res = true;//用于输出正在测试的边的信息。CGAL_SMS_TRACE(3,"Testing topological collapsabilty of p_q=V" << get(m_vim,profile.v0()) << "(%" << degree(profile.v0(), m_tm) << ")"<< "->V" << get(m_vim, profile.v1()) << "(%" << degree(profile.v1(), m_tm) << ")");//输出边是否为边界边的调试信息。CGAL_SMS_TRACE(4, "is p_q border:" << profile.is_v0_v1_a_border());CGAL_SMS_TRACE(4, "is q_q border:" << profile.is_v1_v0_a_border());//定义外围边迭代器,用于遍历与顶点相关联的边。out_edge_iterator eb1, ee1;out_edge_iterator eb2, ee2;//输出边的左右面(如果存在)的相关信息。CGAL_SMS_TRACE(4, "  t=V" << (profile.left_face_exists() ? get(m_vim,profile.vL()) : -1)<< "(%" << (profile.left_face_exists() ? degree(profile.vL(), m_tm) : 0) << ")");CGAL_SMS_TRACE(4, "  b=V" << (profile.right_face_exists() ? get(m_vim,profile.vR()) : -1)<< "(%" << (profile.right_face_exists() ? degree(profile.vR(), m_tm) :0) << ")");//检查边的两个面是否存在。如果左面或右面不存在,则边可能是边界边或者孤立边。// Simple tests handling the case of non-manifold situations at a vertex or edge (pinching)// (even if we advertise one should not use a surface mesh with such features)if(profile.left_face_exists()){if(CGAL::is_border(opposite(profile.v1_vL(), m_tm), m_tm) &&CGAL::is_border(opposite(profile.vL_v0(), m_tm), m_tm))return false;if(profile.right_face_exists() &&CGAL::is_border(opposite(profile.vR_v1(), m_tm), m_tm) &&CGAL::is_border(opposite(profile.v0_vR(), m_tm), m_tm))return false;}else{if(profile.right_face_exists()){if(CGAL::is_border(opposite(profile.vR_v1(), m_tm), m_tm) &&CGAL::is_border(opposite(profile.v0_vR(), m_tm), m_tm))return false;}elsereturn false;}//遍历顶点v0周围的半边,检查这些半边是否满足所谓的“链接条件”。//链接条件是指,对于每个与v0和v1都相邻的顶点k,必须存在一个面包含v0、v1和k。// The following loop checks the link condition for v0_v1.// Specifically, that for every vertex 'k' adjacent to both 'p and 'q', 'pkq' is a face of the mesh.//for(boost::tie(eb1,ee1) = halfedges_around_source(profile.v0(), m_tm); res && eb1 != ee1; ++eb1){halfedge_descriptor v0_k = *eb1;if(v0_k != profile.v0_v1()){vertex_descriptor k = target(v0_k, m_tm);for(boost::tie(eb2,ee2) = halfedges_around_source(k, m_tm); res && eb2 != ee2; ++eb2){halfedge_descriptor k_v1 = *eb2;if(target(k_v1, m_tm) == profile.v1()){// At this point we know p-q-k are connected and we need to determine if this triangle is a face of the mesh.//// Since the mesh is known to be triangular there are at most two faces sharing the edge p-q.//// If p->q is NOT a border edge, the top face is p->q->t where t is target(next(p->q))// If q->p is NOT a border edge, the bottom face is q->p->b where b is target(next(q->p))//// If k is either t or b then p-q-k *might* be a face of the mesh. It won't be if k==t but p->q is border// or k==b but q->b is a border (because in that case even though there exists triangles p->q->t (or q->p->b)// they are holes, not faces)////检查顶点k是否与v0和v1共享一个面。bool is_face = (profile.vL() == k && profile.left_face_exists()) ||(profile.vR() == k && profile.right_face_exists());CGAL_assertion_code(if(is_face){// Is k_v1 the halfedge bounding the face 'k-v1-v0'?if(!is_border(k_v1, m_tm) && target(next(k_v1, m_tm), m_tm) == profile.v0()){CGAL_assertion(target(k_v1, m_tm) == profile.v1());CGAL_assertion(target(next(k_v1, m_tm), m_tm) == profile.v0());CGAL_assertion(target(next(next(k_v1, m_tm), m_tm), m_tm) == k);}else // or is it the opposite?{halfedge_descriptor v1_k = opposite(k_v1, m_tm);CGAL_assertion(!is_border(v1_k, m_tm));CGAL_assertion(target(v1_k, m_tm) == k);CGAL_assertion(target(next(v1_k, m_tm), m_tm) == profile.v0());CGAL_assertion(target(next(next(v1_k, m_tm), m_tm), m_tm) == profile.v1());}});if(!is_face){CGAL_SMS_TRACE(3,"  k=V" << get(m_vim,k) << " IS NOT in a face with p-q. NON-COLLAPSABLE edge.");res = false;break;}else{CGAL_SMS_TRACE(4,"  k=V" << get(m_vim,k) << " is in a face with p-q");}}}}}if(res){//检查边是否与受约束的边相邻。如果是,这可能会阻止边的塌陷。/// ensure two constrained edges cannot get mergedif(is_edge_adjacent_to_a_constrained_edge(profile, m_ecm))return false;//检查边是否是边界边,以及是否满足其他拓扑约束条件。if(profile.is_v0_v1_a_border()){if(is_open_triangle(profile.v0_v1())){res = false;CGAL_SMS_TRACE(3,"  p-q belongs to an open triangle. NON-COLLAPSABLE edge.");}}else if(profile.is_v1_v0_a_border()){if(is_open_triangle(profile.v1_v0())){res = false;CGAL_SMS_TRACE(3,"  p-q belongs to an open triangle. NON-COLLAPSABLE edge.");}}else{if(is_border(profile.v0(), m_tm) && is_border(profile.v1(), m_tm)){res = false;CGAL_SMS_TRACE(3,"  both p and q are boundary vertices but p-q is not. NON-COLLAPSABLE edge.");}else{bool tetra = is_tetrahedron(profile.v0_v1());//CGAL_assertion(tetra == m_tm.is_tetrahedron(profile.v0_v1()));if(tetra){res = false;CGAL_SMS_TRACE(3,"  p-q belongs to a tetrahedron. NON-COLLAPSABLE edge.");}if(next(profile.v0_v1(), m_tm) == opposite(prev(profile.v1_v0(), m_tm), m_tm) &&prev(profile.v0_v1(), m_tm) == opposite(next(profile.v1_v0(), m_tm), m_tm)){CGAL_SMS_TRACE(3,"  degenerate volume.");return false;}}}}return res;
}

 2、loop()

这部分代码比较容易看懂,主要函数就是m_should_stop(*cost, profile, m_initial_edge_count, m_current_edge_count)

这个函数主要用于从一个优先队列中不断取出边,并根据一定的条件和成本判断是否对这些边进行折叠操作,如何可以折叠那么是否在拓扑和几何上有效。从而对模型进行简化

// 在EdgeCollapse类中定义一个模板函数。
// 类的模板参数包括TM, GT, SP, VIM, VPM, HIM, ECM, CF, PF, SI, V。
template<class TM, class GT, class SP, class VIM, class VPM, class HIM, class ECM, class CF, class PF, class SI, class V>
void
EdgeCollapse<TM,GT,SP,VIM,VPM,HIM,ECM,CF,PF,SI,V>::
loop()
{// 输出日志信息,表示开始折叠边CGAL_SMS_TRACE(0, "Collapsing edges...");// 断言代码,用于调试时验证条件。此处检查不可折叠计数。CGAL_assertion_code(size_type non_collapsable_count = 0);// 从优先队列中弹出并处理每条边boost::optional<halfedge_descriptor> opt_h;// 如果定义了CGAL_SURF_SIMPL_INTERMEDIATE_STEPS_PRINTING,则启用中间步骤打印
#ifdef CGAL_SURF_SIMPL_INTERMEDIATE_STEPS_PRINTINGint i_rm = 0;
#endif// 当优先队列非空时循环while((opt_h = pop_from_PQ())){// 输出日志信息,显示弹出的边CGAL_SMS_TRACE(1, "Popped " << edge_to_string(*opt_h));// 断言检查边是否不受约束CGAL_assertion(!is_constrained(*opt_h));// 创建边的Profileconst Profile profile = create_profile(*opt_h);// 获取边的成本Cost_type cost = get_data(*opt_h).cost();// 调用访问者模式的函数m_visitor.OnSelected(profile, cost, m_initial_edge_count, m_current_edge_count);// 如果成本存在if(cost){// 如果满足停止条件if(m_should_stop(*cost, profile, m_initial_edge_count, m_current_edge_count)){// 调用访问者模式的函数,表示达到停止条件m_visitor.OnStopConditionReached(profile);// 输出日志信息,显示达到停止条件的信息CGAL_SMS_TRACE(0, "Stop condition reached with initial edge count=" << m_initial_edge_count<< " current edge count=" << m_current_edge_count<< " current edge: " << edge_to_string(*opt_h));break;}// 如果折叠在拓扑上有效if(is_collapse_topologically_valid(profile)){// 获取放置位置Placement_type placement = get_placement(profile);// 如果折叠在几何上有效if(is_collapse_geometrically_valid(profile, placement)){// 如果定义了CGAL_SURF_SIMPL_INTERMEDIATE_STEPS_PRINTING,则打印步骤信息
#ifdef CGAL_SURF_SIMPL_INTERMEDIATE_STEPS_PRINTINGstd::cout << "step " << i_rm << " " << get(m_vpm, source(*h, m_tm))<< " " << get(m_vpm, target(*h, m_tm)) << "\n";
#endif// 如果不应该忽略此折叠if(m_should_ignore(profile, placement)!= boost::none){// 执行折叠操作collapse(profile, placement);}else{// 如果不能折叠,则增加不可折叠计数CGAL_assertion_code(++non_collapsable_count);// 调用访问者模式的函数,表示边不可折叠m_visitor.OnNonCollapsable(profile);// 输出日志信息,显示边不可折叠CGAL_SMS_TRACE(1, edge_to_string(*opt_h) << " NOT Collapsable" );}// 如果定义了CGAL_SURF_SIMPL_INTERMEDIATE_STEPS_PRINTING,则保存中间步骤的文件
#ifdef CGAL_SURF_SIMPL_INTERMEDIATE_STEPS_PRINTINGstd::stringstream sstr;sstr << "debug/P-";if(i_rm<10) sstr << "0";if(i_rm<100) sstr << "0";sstr << i_rm << ".off";std::ofstream out(sstr.str().c_str());out << m_tm;++i_rm;
#endif}}else{// 如果不能折叠,则增加不可折叠计数CGAL_assertion_code(++non_collapsable_count);// 调用访问者模式的函数,表示边不可折叠m_visitor.OnNonCollapsable(profile);// 输出日志信息,显示边不可折叠CGAL_SMS_TRACE(1, edge_to_string(*opt_h) << " NOT Collapsable" );}}else{// 输出日志信息,显示成本不可计算CGAL_SMS_TRACE(1, edge_to_string(*opt_h) << " uncomputable cost." );}}
}

 

这篇关于CGAL内置的边塌陷算法代码解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/648294

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n