1024 [SCOI2010]游戏 贪心|dfs|并查集
链接:https://ac.nowcoder.com/acm/contest/23156/1024 来源:牛客网
题目描述 lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。 游戏进行到最后,lxhgww遇到了终极boss,这个终极boss很奇怪,攻击他的装备所使用的属性值必须从1开始连续递增地攻击,才能对boss产生伤害。也就是说一开始的时候,lxhgww只能使用某个属性值为1的装备攻击boss,然后只能使用某个属性值为2的装备攻击boss,然后只能使用某个属性值为3的装备攻击boss……以此类推。现在lxhgww想知道他最多能连续攻击boss多少次? 输入描述:
输入的第一行是一个整数N,表示lxhgww拥有N种装备 接下来N行,是对这N种装备的描述,每行2个数字,表示第i种装备的2个属性值
输出描述:
输出一行,包括1个数字,表示lxhgww最多能连续攻击的次数。
示例1 输入 复制
3 1 2 3 2 4 5
输出 复制
2
备注:
对于30%30%30% 的数据,保证N≤1000N \leq 1000N≤1000 对于100%100%100% 的数据,保证N≤1000000N \leq 1000000N≤1000000
贪心:
是我搞错了还是大佬搞错了。这题直接贪心不就行了,为啥要二分图匹配,。。。
给每个节点按照比较小的关键字排序,然后比较大的关键字排序不排序都行
然后从前往后查找每个1,2,3,4,5.。。和每个节点一一比较
如果每个节点的a和b都不满足条件,把这个b保存下来,如果下次遇到了这个b值直接使用它
就a了啊。
ps:关于,一开始我还在想这题的意思是1,2,3,4,5这样连续的最长长度还是找到某个不适配的值再倒回来12345,想写个《比较》暴力的写法试一下,没想到就a了这件事,嘿嘿嘿
dfs:
将所有点连起来,然后深搜,如果搜不到重复的数,说明不是树,取这个连通块走过的最大值
并查集:
开一个数组存每个连通块的最大值
如果说一个连通块里有两个值重复出现,说明连通块里的值是连成了树的,这个连通块的所有值都能走
遍历所有连通块,找到不是数的,最大值最小的连通块的最大值,-1就是结果(因为只是一棵树,边数 = 点数 - 1 ,边不够,最后一个点取不到)
//-------------------------代码----------------------------
//#define int LL
const int N = 1e6+10;
int n,m;
struct Q {
int a,b;
bool operator<(const Q & t )const {
if(a == t.a) {
return b < t.b;
} else return a < t.a;
}
};
Q q[N];
int vis[N];
void solve()
{
cin>>n;
// V<V<int>>mp(n+1,V<int>(m+1));
fo(i,1,n) {
cin>>q[i].a>>q[i].b;
if(q[i].a > q[i].b)swap(q[i].a,q[i].b);
}
sort(q+1,q+1+n);
int sum = 0;
int l = 0;
for(int i = 1;i<=n;i++) {
while(vis[sum + 1]) sum ++ ,l ++ ;
if(q[i].a == 1+sum) {
sum ++ ; l ++ ;
} else if(q[i].b == 1 + sum) {
sum ++ ; l ++ ;
} else vis[q[i].b] = true;
}
cout<<l<<endl;
}
signed main(){
clapping();TLE;
// int t;cin>>t;while(t -- )
solve();
// {solve(); }
return 0;
}
/*样例区
*/
//------------------------------------------------------------
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;
}
题目描述 lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。 游戏进行到最后,lxhgww遇到了终极boss,这个终极boss很奇怪,攻击他的装备所使用的属性值必须从1开始连续递增地攻击,才能对boss产生伤害。也就是说一开始的时候,lxhgww只能使用某个属性值为1的装备攻击boss,然后只能使用某个属性值为2的装备攻击boss,然后只能使用某个属性值为3的装备攻击boss……以此类推。现在lxhgww想知道他最多能连续攻击boss多少次? 输入描述:
输入的第一行是一个整数N,表示lxhgww拥有N种装备 接下来N行,是对这N种装备的描述,每行2个数字,表示第i种装备的2个属性值
输出描述:
输出一行,包括1个数字,表示lxhgww最多能连续攻击的次数。
示例1 输入 复制
3 1 2 3 2 4 5
输出 复制
2
备注:
对于30%30%30% 的数据,保证N≤1000N \leq 1000N≤1000 对于100%100%100% 的数据,保证N≤1000000N \leq 1000000N≤1000000
贪心:
是我搞错了还是大佬搞错了。这题直接贪心不就行了,为啥要二分图匹配,。。。
给每个节点按照比较小的关键字排序,然后比较大的关键字排序不排序都行
然后从前往后查找每个1,2,3,4,5.。。和每个节点一一比较
如果每个节点的a和b都不满足条件,把这个b保存下来,如果下次遇到了这个b值直接使用它
就a了啊。
ps:关于,一开始我还在想这题的意思是1,2,3,4,5这样连续的最长长度还是找到某个不适配的值再倒回来12345,想写个《比较》暴力的写法试一下,没想到就a了这件事,嘿嘿嘿
dfs:
将所有点连起来,然后深搜,如果搜不到重复的数,说明不是树,取这个连通块走过的最大值
并查集:
开一个数组存每个连通块的最大值
如果说一个连通块里有两个值重复出现,说明连通块里的值是连成了树的,这个连通块的所有值都能走
遍历所有连通块,找到不是数的,最大值最小的连通块的最大值,-1就是结果(因为只是一棵树,边数 = 点数 - 1 ,边不够,最后一个点取不到)
//-------------------------代码----------------------------
//#define int LL
const int N = 1e6+10;
int n,m;
struct Q {
int a,b;
bool operator<(const Q & t )const {
if(a == t.a) {
return b < t.b;
} else return a < t.a;
}
};
Q q[N];
int vis[N];
void solve()
{
cin>>n;
// V<V<int>>mp(n+1,V<int>(m+1));
fo(i,1,n) {
cin>>q[i].a>>q[i].b;
if(q[i].a > q[i].b)swap(q[i].a,q[i].b);
}
sort(q+1,q+1+n);
int sum = 0;
int l = 0;
for(int i = 1;i<=n;i++) {
while(vis[sum + 1]) sum ++ ,l ++ ;
if(q[i].a == 1+sum) {
sum ++ ; l ++ ;
} else if(q[i].b == 1 + sum) {
sum ++ ; l ++ ;
} else vis[q[i].b] = true;
}
cout<<l<<endl;
}
signed main(){
clapping();TLE;
// int t;cin>>t;while(t -- )
solve();
// {solve(); }
return 0;
}
/*样例区
*/
//------------------------------------------------------------
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;
}