并查集/搜索 SCOI2010]游戏

链接:登录—专业IT笔试面试备考平台_牛客网

考虑将所有的装备属性值看作点,每一个装备看作点与点之间的线,那么情况就是:对于每一条先线,只能在其端点上取其中一个点,然后要保证取的点是连续增大的,问最多能枚举到哪个点。应该注意到可能并不是所有点都是互相连在一起的,也就是说,有的点可能是跟其他所有点相互孤立的,所以,我们应当求出所有的连通块,在每一个连通块内部求出其能枚举到的最大的点然后把所有连通块的结果在最后连起来,找到最小的最大枚举不到的点,它-1就是答案了。

那么关于这个连通块的处理,就可以用并查集或者搜索来处理。

用并查集:
如果枚举到两个点用一条线连起来,那么它们就应该放进一棵树,在此之前先进行一0次判定,如果它们之前没连过,那么就连上,联通怪=块内加一条边。但如果它们已经在一棵树里的了,就意味着,二者之间会再放入一条边,那么这个连通块的形状就不再是一个环了,换句话说,连通块内部所有点都能被枚举到(详见雨巨),可以在之前立一个flag,用来表示这个连通块是一个环(边数=点数-1)或者就是边数>=点数(所有点都可取)。

#include<bits/stdc++.h>
using namespace std;
int n,q,s;
int fa[10010],maxn[10010],bo[10010];//maxn:以i为根的连通块的第一个枚举不了的数
//bo:返回连通块是否是一个环 
int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=10010;++i){
		fa[i]=i;
		maxn[i]=i;
		bo[i]=0;
	}
	for(int i=1;i<=n;++i){
		cin>>q>>s;
		int fq=find(q);
		int fs=find(s);
		if(fq!=fs){
			fa[fq]=fs;
			maxn[fs]=max(maxn[fs],maxn[fq]);
			bo[fs]|=bo[fq];
		}
		else bo[fs]=1;
	}
	int ans=10001;
	for(int i=1;i<=10000;++i){
		if(fa[i]==i){//x是根节点 ,我们只需考虑根节点,因为信息全在根节点
			if(!bo[i]){//如果是环,这个连通块内就必须舍弃一个点,我们把这个最小点更新。
//如果不是环,所有点都可以枚举到,那就没必要去更新结果了。
				ans=min(ans,maxn[i]);
			}
		} 
		 
	}
    cout<<ans-1<<endl;
	return 0;
}

搜索版:

#include<bits/stdc++.h>
using namespace std;
int n;
int x,y,m=0;
int maxn=0;
int vis[10010];
vector<int> vt[10010];
bool dfs(int x,int fa)
{
	vis[x]=1;
	maxn=max(x,maxn);
	int flag=0;
	for(int i=0;i<vt[x].size();i++){
        int f=vt[x][i];
		if(f==fa) continue;
		if(vis[f]==1)  {flag=1;continue;}
		if(dfs(f,x)==1) flag=1;
	}
	return flag;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
    	cin>>x>>y;
    	vt[x].push_back(y);
    	vt[y].push_back(x);
    	m=max(x,m);
    	m=max(y,m);
	}
	memset(vis,0,sizeof(vis));
    int ans=m+1;
	for(int i=1;i<=m;i++)
	{
		maxn=0;
		if(!vis[i]&&!dfs(i,0))
		ans=min(ans,maxn);
	}
	cout<<ans-1<<endl;
	return 0; 
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值