题意:
你有n种装备,每种装备有两个属性值,你要在这两个属性值里选一个,使得这些属性值是1、2、3、4......这些从1开始连续的数,题目求最多能连续到多少。
思路:
假设这些连在一起的数都在一个连通块里,那么如果这个连通块内有大于n-1条边,连通块内里的数就都能用上,否则就只能用到这个连通块内第二大的数。可以用并查集来维护连通块,也可以用dfs来维护。
代码:
dfs
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N=10100;
int n;
vector<int> edge[N];
bool vis[N];
int maxn=0;
bool dfs(int x,int fa)//fa是从哪一个点来的
{
maxn=max(x,maxn);//找这个连通块内最大的数
vis[x]=1;
int flag=0;//看一下是棵树,还是比树的边数还多
for(int i=0;i<edge[x].size();i++)
{
int j=edge[x][i];
if(j==fa) continue;//如果它们是一个环,那就跳过
if(vis[j]) {flag=1;continue;}//如果之前走过,那么说明这个连通块内的边比树的边数多。
if(dfs(j,x)) flag=1;
}
return flag;
}
int main(){
cin>>n;
int m=0;
while(n--)
{
int a,b;
cin>>a>>b;
edge[a].push_back(b);
edge[b].push_back(a);
m=max(m,a);
m=max(m,b);
}
int ans=m+1;//ans是假设比最大还大1的攻击次数
memset(vis,0,sizeof vis);
for(int i=1;i<=m;i++)//去搜索每个点所在的连通块
{
maxn=0;//每次都要清零
if(!vis[i] && !dfs(i,0))//如果这个点所在的连通块是一棵树,就去更新答案,如果比树还多一条边,那么这个连通块内的点都能到,就不用任何操作了。
ans=min(ans,maxn);
//cout<<ans<<endl;
}
cout<<ans-1<<endl;
return 0;
}
并查集
#include<iostream>
#include<algorithm>
using namespace std;
const int N=10010;
int fa[N],maxn[N];//maxn是比各个根结点最大的攻击次数还多1的攻击次数
bool b[N];//b是看各个根结点是不是有比n-1条边还多的边
int n;
int find(int x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
int main(){
for(int i=1;i<N;i++)
fa[i]=i,maxn[i]=i;
cin>>n;
for(int i=0;i<n;i++)
{
int x,y;
cin>>x>>y;
int fx=find(x);
int fy=find(y);
if(fx != fy)//如果根结点不一样
{
fa[fx]=fy;
maxn[fy]=max(maxn[fx],maxn[fy]);//因为根结点变成fy,所以只用更新fy
b[fy]=(b[fx] || b[fy]);//两个集合中只要有一个是有多至少一条边的,那么这两个集合合并后,集合里的每个数都能用上
}
else//根结点一样
{
b[fx]=1;//多连了一条边
}
}
int ans=10001;
for(int i=1;i<=10000;i++)
{
if(fa[i] == i)
{
if(b[i]==0)//如果集合内不能用上所有数
ans=min(ans,maxn[i]);
}
}
cout<<ans-1<<endl;
return 0;
}