计科练习12题解(并查集,最小生成树)

目录

黑暗意志

闪闪发光

最小生成树(Prim)

并查集

隔离

最小生成树(Kruskal)


黑暗意志

题目描述

在数千年前潘达利亚从卡利姆多分离之时,迷雾笼罩着这块新形成的大陆,使它不被外来者发现。迷雾同样遮蔽着这片大陆古老邪恶的要塞——雷神的雷电王座。在雷神统治时期,他的要塞就是雷电之王力量的保障。即使在他死后,强大而堕落的能量仍然在此萦绕不绝;而复活的雷电之王和赞达拉巨魔企图再次驾驭那股能量,重建昔日帝国。这就是魔兽世界5.2版本的大型团队副本"雷电王座"
雷电王座中有一个神奇的BOSS是所有玩家都会记得的,直到现在当你的人物血量比BOSS血量都高的时候,在战斗的时候都要注意自己是不是会一不留神死在这个BOSS面前,它就是"黑暗意志"
黑暗意志的整场战斗围绕着一种神秘、旋转的液体——心能;它们是在这侧厅室内魔古实验的核心。这些重要的液体有固定的量并能激活魔像。当最初储藏它们的容器被摧毁后,逃逸出的液体会不断流向越来越大的构造体;直至它们的量聚集到一定程度后,便会激活所有魔像中最大的一个——黑暗意志。
击杀这个BOSS需要团队玩家一个人面向一个小魔像,在保证不击杀小魔像的情况下存活并且对BOSS进行输出,并且在需要时击杀小魔像以减轻治疗压力,提高对BOSS的输出
但是在炼狱难度下,玩家不但要保证不击杀小魔像,更要保证任意一个小魔像本身的血量,有另一个小魔像的血量与它相同,那么这两个小魔像之间便会形成一条连线(每个小魔像只能连接一条线),同时小魔像上会出现一个debuff(减少小魔像50%的输出),显然这个debuff在炼狱难度下的副本战斗中至关重要。
所以在炼狱难度开荒的时候,队长会给两个DPS差不多的玩家组队,让这两个玩家保证自己的小魔像与另一个人的小魔像血量相同,但是在团灭五次之后,团长终于发现,不是治疗不够,而是有两个玩家的DPS与之前的DPS有了差别,所以总是不能保证他们这一组的小魔像血量相同,但是这个时候,每对玩家彼此都有了默契,重新分组显然是不明智的,那么团长准备自己去帮助那两个玩家,保证他们的小魔像血量相同,但是团长需要知道,他们两个的小魔像血量是多少,才可以去攻击那个血量较多的小魔像,来保证这两个小魔像血量相同,从而击杀BOSS。
所以现在告诉你所有小魔像的血量,你能告诉团长,那两个玩家所对应的小魔像血量是多少吗?

输入

第一行一个整数T,代表数据的组数(1<=T<=10),接下来T组数据,每组数据的第一行是一个整数n(2<=n<=1000000,保证n是偶数),第二行是n个整数ai(0 <= ai <= 1000000000)代表血量,每两个整数之间有一个空格,(输入保证有且只有两个小魔像无法连线)

输出

对于每组数据,输出两个整数,分别代表两个小魔像的血量,中间有一个空格,并且血量较大的先输出。

样例输入 Copy

2
6
2 2 1 1 3 4
4
1 1 3 4

样例输出 Copy

4 3
4 3
#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
int main()
{
    int i;
    int t;
    while(cin>>t){
        while(t--){
            int n;
            cin>>n;
            int a[n];
            for(i=0;i<n;++i){
                cin>>a[i];
            }
            sort(a,a+n);
            int ans=0;
            int b[2];
            for(i=0;i<n;++i){
                if(a[i]==a[i+1]){
                    ++i;
                }else{
                    b[ans++]=a[i];
                }
            }
            cout<<b[1]<<" "<<b[0]<<endl;
        }
    }
    return 0;
}

闪闪发光

题目描述

一所位于云南昆明的中医药本科院校--云南中医学院。

因为报考某专业的人数骤减,正面临着停招的危机。

其中有九名少女想到一条妙计——成为偶像,

只要她们成为偶像,学校的名气便会增加,而报考的人数亦会上升。

就这样,九位个性鲜明的少女决定一起努力成为偶像。

希望可以凭借偶像的名气增加生源来挽救自己所喜爱的专业,让自己的学校闪闪发光。

她们为了成为偶像,第一步对于她们来说是减肥!

这里有n个重物,第i个重物的重量是2^{w_i}。她们每天任务要完成的重量是n个重物的重量和。

每次举重的重量和必须是2的幂,重物数量不要求。

但是为了方便,要使举重的次数最少。

输入

多组数据。
每组数据第一行一个整数n。(1 <= n <= 10^6)
第二行有n个整数w_1,w_2,...,w_n。(0 <= w_i <= 1000000)

输出

输出最少的举重次数。

样例输入 Copy

5
1 1 2 3 3

样例输出 Copy

2

提示

1,1,2一组;
3,3一组。

#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
set<int> s;
int main()
{
    int i;
    int t;
    while(cin>>t){
        s.clear();
        int n;
        for(i=0;i<t;++i){
            cin>>n;
            if(s.find(n)!=s.end()){
                do
                {
                    s.erase(n++);
                }
                while(s.find(n)!=s.end());
            }
            s.insert(n);
        }
        cout<<s.size()<<endl;
    }
    return 0;
}

最小生成树(Prim)

题目描述

使用Prim算法求图的最小生成树(MST)

输入

每组数据分为两个部分,第一部分为图的点数n,和边数m,
第二部分为m行,每一行输入三个数字,前两个为两个顶点的编号,第三个为边权重。

输出

最小生成树,输出时按照边的两个端点的升序输出。(先看左端点,再看右端点,端点不换位置)

样例输入 Copy

3 3
0 1 10
0 2 15
1 2 50

样例输出 Copy

0 1 10
0 2 15
#include<bits/stdc++.h>
using namespace std;
#define maxn 10000
#define MAX 1000
int a[MAX][MAX];
void prim(int n)
{
    int lowcost[MAX],closest[MAX],s[MAX];
    int i,j,k;
    s[1]=1;
    for(i=2;i<=n;i++){
        lowcost[i]=a[1][i];
        closest[i]=1;
        s[i]=0;
    }

    for(i=1;i<n;i++){
        int Min=maxn;
        j=1;
        for(k=1;k<=n;k++)
            if(s[k]==0&&lowcost[k]<Min){
                Min=lowcost[k];
                j=k;
        }
        if(a[closest[j]][j]>0)
            cout<<closest[j]-1<<" "<<j-1<<" "<<a[closest[j]][j]<<endl;
        else
            cout<<j-1<<" "<<closest[j]-1<<" "<<-1*a[closest[j]][j]<<endl;
        s[j]=1;
        for(k=2;k<=n;k++){
            if(a[j][k]<lowcost[k]&&s[k]==0){
            lowcost[k]=fabs(a[j][k]);
            closest[k]=j;
            }
        }
    }
}
int main()
{
    int x,y,z;
    int n,m;
    int i,j;
    while(cin>>n>>m){
        memset(a,0,sizeof(a));
        for(i=1;i<=n;i++){
            for(j=1;j<=n;j++){
                a[i][j] = maxn;
            }
        }
        for(i=1;i<=m;i++){
            cin>>x>>y>>z;
            ++x;
            ++y;
            a[x][y]=z;
            a[y][x]=-1*z;
        }
        prim(n);
    }
    return 0;
}

并查集

题目描述

在某个城市中住着n个人,现在给定关于这n个人的m条信息(即某2个人认识)。

假设所有认识的人一定属于同一个单位,请计算该城市有多少个单位?

输入

第1行的第1个值表示总人数n,第2个值表示总信息数m;第2行开始为具体的认识关系信息

输出

单位的个数

样例输入 Copy

10 4
2 3
4 5
4 8
5 8

样例输出 Copy

7
#include<bits/stdc++.h>
using namespace std;
#define maxn 100001
int f[maxn];
int r[maxn];
int ans;
void mset(int m){
    for(int i=0;i<m;++i){
        f[i]=i;
        r[i]=0;
    }
}
int fset(int num){
    if(num!=f[num]){
        f[num]=fset(f[num]);
    }
    return f[num];
}
void uset(int a,int b){
    a=fset(a);
    b=fset(b);
    if(a==b)
        return ;
    ans--;
    if(r[a]>r[b]){
        f[b]=a;
    }else{
        f[a]=b;
        if(r[a]=r[b])
            r[b]++;
    }
}
int main(){
    int i,j;
    int m,n;
    while(cin>>m>>n){
        mset(m);
        int a,b;
        ans=m;
        for(i=0;i<n;++i){
            cin>>a>>b;
            uset(a,b);
        }
        cout<<ans<<endl;
    }
    return 0;
}

隔离

题目描述

如果一个社团中有一个学生疑似非典病人,那么该社团中所有学生都将被隔离。

假定编号为0的学生疑似非典病人,统计将被隔离的学生总人数(包括编号为0的学生)。

输入

第1行的第1个数字n表示学生总人数,第2个数字m表示社团个数;从第2行开始,接下来的m行表示每个社团的成员总人数和成员编号,每一行的第1个数字k表示该社团成员总人数,接下来的k个数字表示每一个成员的编号

输出

被隔离学生总人数

样例输入 Copy

100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2

样例输出 Copy

4

#include<bits/stdc++.h>
using namespace std;

#define maxn 100005
int n,m;
int f[maxn];

void mset()
{
    for(int i=0;i<=n;i++)
        f[i]=i;
}
int fset(int num)
{
    return f[num]==num?f[num]:fset(f[num]);
}
void uset(int a,int b)
{
    int aa=fset(a);
    int bb=fset(b);
    if(aa!=bb){
        f[aa]=bb;
    }
}
int main()
{
    int a,b;
    while(cin>>n>>m&&(m||n)){
        mset();
        for(int i=0;i<m;i++){
            int t;
            cin>>t;
            cin>>a;
            for(int j=1;j<t;j++){
                cin>>b;
                uset(a,b);
            }
        }
        int ans=0;
        for(int i=0;i<n;i++){
            if(fset(i)==fset(0)){
                ans++;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

最小生成树(Kruskal)

题目描述

编程实现Kruskal算法,求图的最小生成树(MST)

输入

每组数据分为两个部分,第一部分为图的点数n,和边数m, 
第二部分为m行,每一行输入三个数字,前两个为两个顶点的编号,第三个为边权重。 

输出

最小生成树,输出时按照边的两个端点的升序输出。(先看左端点,再看右端点,端点不换位置)

样例输入 Copy

3 3
0 1 10
0 2 15
1 2 50

样例输出 Copy

0 1 10
0 2 15
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
int n,m;
int f[maxn];
typedef struct mystruct
{
    int x,y,z;

};
mystruct a[maxn],b[maxn];
bool cmp(mystruct a, mystruct b)
{
    return a.z<b.z;
}
bool cmp1(mystruct a, mystruct b)
{
    if(a.x!=b.x){
        return a.x<b.x;
    }else{
        return a.y<b.y;
    }
}
void mset()
{
    for(int i=0;i<=n;i++)
        f[i]=i;
}
int fset(int num)
{
    return f[num]==num?f[num]:fset(f[num]);
}
void kurskal()
{
    int i;
    mset();
    int ans=0;
    for(i=0;i<m;i++){
        int aa=fset(a[i].x);
        int bb=fset(a[i].y);
        if(aa!=bb){
            f[aa]=bb;
            b[ans].x=a[i].x;
            b[ans].y=a[i].y;
            b[ans++].z=a[i].z;
        }
    }
    sort(b,b+ans,cmp1);
    for(i=0;i<ans;i++){
        cout<<b[i].x<<" "<<b[i].y<<" "<<b[i].z<<endl;
    }
}
int main()
{
    int i;
    while(cin>>n>>m){
        for(i=0;i<m;i++){
            cin>>a[i].x>>a[i].y>>a[i].z;
        }
        sort(a,a+m,cmp);
        kurskal();
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值