P2661(拓扑排序找最小环)

#include<iostream>
#include<queue>
using namespace std;
const int maxn=400000;
struct E{
	int to,nxt;
}edge[maxn];
int head[maxn],cnt,in[maxn],visit[maxn];
void add_edge(int x1,int x2){
	edge[++cnt].to=x2;
	edge[cnt].nxt=head[x1];
	head[x1]=cnt;
}
void topo(int n){
	queue<int>q;
	for(int i=1;i<=n;i++)
	if(in[i]==0) q.push(i);
	vector<int>ans;
	while(!q.empty()){
		int p=q.front();
		q.pop();
		ans.push_back(p);
		for(int i=head[p];i;i=edge[i].nxt){
			int y=edge[i].to;
			in[y]--;
			if(in[y]==0)
			q.push(y);
		} 
	}
	for(int i=0;i<ans.size();i++)
	visit[ans[i]]=1;
}
int dfs(int m){
	visit[m]=1;
	for(int i=head[m];i;i=edge[i].nxt)
	{
		int y=edge[i].to;
		if(visit[y]==0)
		{
			return dfs(y)+1;
		}
	}
	return 1;
} 
int main(){
	int n,x1,x2,x3,ans1;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>x1;
		add_edge(i,x1);
		in[x1]++;
	}
	topo(n);
	int m=maxn;
	for(int i=1;i<=n;i++){
		if(visit[i]==0)
		{
			m=min(m,dfs(i));
		}
	}
	cout<<m<<endl;
	return 0;
}
### 关于拓扑排序中使逆序数最小的算法 在处理拓扑排序时,通常的目标是到一种满足所有依赖关系的节点排列方式。然而,在某些情况下,可能还需要考虑额外的要求,比如使得最终序列中的 **逆序数** 尽量小。 #### 什么是逆序数? 在一个排列 \( P \) 中,如果存在两个索引 \( i < j \),而对应的值却满足 \( P[i] > P[j] \),那么这对 \( (P[i], P[j]) \) 被称为一个逆序对。对于给定的一个排列,其所有的逆序对总数即为该排列的逆序数[^1]。 当讨论拓扑排序时,“逆序数最小”的含义是指:在所有合法的拓扑顺序中,选出那个具有最少逆序对的排列作为结果。 --- #### 解决方案概述 为了实现这一目标,可以采用如下方法: 1. **优先队列的选择** 使用一个小顶堆(`priority_queue<int, vector<int>, greater<int>>` 或者 `std::set`)来存储当前入度为零的节点集合。这样每次都会选取编号较小的节点加入到结果序列中,从而有助于减少后续产生的逆序对数量[^4]。 2. **调整标准库函数行为** 如果使用 C++ 的 STL 容器,则可以通过自定义比较器或者直接利用已有的容器特性完成上述逻辑。例如: ```cpp // 自定义比较器用于维护升序的小根堆 auto cmp = [](const int& a, const int& b) -> bool { return a > b; }; ``` 3. **具体实现步骤** - 初始化阶段读取输入边信息并构建邻接表表示图结构; - 计算各点初始入度并向合适的数据结构填充可用起点候选列表; - 循环执行直到无剩余未访问结点为止——依次弹出入度清零项、更新关联邻居状态以及重新评估新符合条件成员是否可纳入下一轮迭代范围之内;最后验证所得路径长度等于预期总规模即可确认成功构造有效解。 以下是基于以上思路的具体代码示例: ```cpp #include <bits/stdc++.h> using namespace std; struct Edge { int from, to; }; // 主程序入口 int main(){ ios::sync_with_stdio(false); cin.tie(0); int T;cin >> T; while(T--){ int N,M; cin >> N >> M; // 构建图和计算入度 vector<vector<int>> adj(N+1); vector<int> indegree(N+1,0); for(int i=0;i<M;++i){ int u,v; cin >> u >> v; adj[u].push_back(v); ++indegree[v]; } // 准备优先级队列 priority_queue<int,vector<int>,greater<int>> pq;// 默认是最小堆 for(int node=1;node<=N;node++)if(!indegree[node])pq.push(node); vector<int> topo_order; while(!pq.empty()){ int current=pq.top(); pq.pop(); topo_order.push_back(current); for(auto neighbor:adj[current]){ --indegree[neighbor]; if(indegree[neighbor]==0)pq.push(neighbor); } } if(topo_order.size()!=N){ cout << "IMPOSSIBLE\n"; } else{ for(auto num:topo_order)cout<<num<<" "; cout<<"\n"; } } } ``` 此版本通过引入更精确控制机制实现了按数值大小优选原则下的拓扑排序过程,进而间接达成降低整体逆序数的目的。 --- #### 进一步说明与注意事项 尽管这种方法能够显著改善部分场景下的表现效果,但在极端条件下仍可能存在无法完全消除全部潜在冲突的情况。因此实际应用过程中还需结合具体情况加以权衡考量。 另外值得注意的是,本解答主要围绕理论层面展开探讨,并假设输入数据均符合简单有向无环图(DAG)特征前提条件之下得出结论。针对复杂情形或特殊需求则需另行设计针对性更强的技术手段予以应对解决。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值