并查集/搜索 SCOI2010]游戏

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

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

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

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

#include
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<

搜索版:

#include
using namespace std;
int n;
int x,y,m=0;
int maxn=0;
int vis[10010];
vector vt[10010];
bool dfs(int x,int fa)
{
	vis[x]=1;
	maxn=max(x,maxn);
	int flag=0;
	for(int i=0;i>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<

你可能感兴趣的:(搜索,c++,深度优先,动态规划)