P2661 [NOIP 2015 提高组] 信息传递——染色做法
原题
本来想当水题刷的,结果被水题刷了。。。70到80到90到100,必须写个题解记录一下(doge)
题目分析
一句话:求一个无权有向图中的最短环路(确保有环)
tip:每一个点出度为一,那么必然有环,以样例为例如下。
思路
没必要每轮模拟全部的传送,只看某一个人的传送过程:
就1而言:他的信息一次经过1-2-4-3-2...就无法回到1处,时间理解为∞。
就2而言:2-4-3-2,需要经过3轮,时间便为3。
就4,3而言:与2一样都在环里,也是3。
就5而言:不在环里是∞。
综上最快3轮结束游戏。
tip:这些题从不同点上看会比宏观看有奇效。
接下来我就想到了用染色法(万恶的开始),Son[i]记录i的传输对象,col[i]记录i的颜色(col=color),依次遍历每个点,在遍历第i个点时,把i染色成1,再找下一个也就是Son[i],然后Son[Son[i]]...如果发现某个p点的下一个点son[p]==1,说明已经被遍历过,侧面反映成为了环。
在找到环后对从当前的开始给环染第二遍色(p一定在环里,感性理解)每个环里的点都变成2,并且每染一次用一个变量记录,便是当前i收到自己的信息和游戏结束需要的时间。
但当时我嫌麻烦,就每读取一次i就初始化一次col[ ],结果爆掉了(难受)。所以尝试优先队列优化,果然多过了一个样例,然后艰难改到90分,最后终于修成正解!
1.减少染色次数:在原始代码中,每个节点被染色两次(1和2),优化后的代码中,每个节点只被染色一次,减少了不必要的操作。
2.最小环长度的记录:使用一个变量minn 来记录最小环的长度,避免了使用优先队列的开销。
3.循环优化:在检测环的过程中,使用 do-while 循环来确保环的长度计算正确,并且减少了不必要的条件判断。
代码过程
70分暴力染色代码如下
#include<bits/stdc++.h>
using namespace std;
const int N=20005;
int ans=10000008,cnt=0;
int fa[N],col[N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>fa[i];
}
for(int i=1;i<=n;i++){
cnt=0;
col[i]=1;
int p=i;
while(col[fa[p]]!=1){
p=fa[p];
col[p]++;
}
while(col[fa[p]]!=2){
p=fa[p];
col[p]=2;
cnt++;
}
ans=min(ans,cnt);
memset(col,0,sizeof col);
}
cout<<ans;
return 0;
}
然后就...
呜呜呜...
80分暴力+优先队列优化代码
//在思考后,选用优先队列来代替min...
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int cnt=0,Son[N],col[N];
priority_queue<int,vector<int>,greater<int>> ans;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>Son[i];
}
for(int i=1;i<=n;i++){
if(col[i]) continue;
cnt=0;
col[i]=1;
int p=i;
while(col[Son[p]]!=1){
p=Son[p];
col[p]=1;
}
if(col[p]==1){
while(col[Son[p]]!=2){
p=Son[p];
col[p]=2;
cnt++;
}
ans.push(cnt);
}
p=i;
while(col[p]<2){
col[p]=2;
p=Son[p];
}
}
while(ans.top()==1){
ans.pop();
}
cout<<ans.top();
return 0;
}
难受...
最后,优化了染色次数,终于AC!!!
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int Son[N],col[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>Son[i];
}
int minn=N;
for(int i=1;i<=n;i++){
if(col[i]) continue;
int cnt=0;
int p=i;
while(!col[p]){
col[p]=1;
p=Son[p];
}
if(col[p]==1){
int start=p;
do{
cnt++;
p=Son[p];
}while(p!=start);
minn=min(minn,cnt);
}
p=i;
while(col[p]==1){
col[p]=2;
p=Son[p];
}
}
if(minn==N){
cout<<0;
}else{
cout<<minn;
}
return 0;
}
接下来也会更新拓扑做法,敬请期待!
谢谢观看★,°:.☆( ̄▽ ̄)/$:.°★ 。
本文作者:ccgc718
本文链接:https://www.cnblogs.com/ccgc718/p/18730796
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步