欢迎来到ccgc718的博客|

ccgc718

园龄:1个月粉丝:1关注:3

📂图论
🔖题解
2025-02-22 13:08阅读: 10评论: 0推荐: 0

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 中国大陆许可协议进行许可。

posted @   ccgc718  阅读(10)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起