天天快乐编程杯中学生信奥联赛(202002) 题解

此博客原文地址:https://www.cnblogs.com/BobHuang/p/12312129.html

1.6172: Alice视察

本题目比较困难,我们将信息传递出去,而且要满足最短的,那么其实只需要n-1条边,是一棵无向树。在数据结构中有“最小生成树”,有两个算法,分别是Kruskal(加边)及Prime(加点)。这个题目数据量比较小,不卡算法,你可以实现任一算法AC。题解所用的是Kruskal。
这个算法的思想可以这样理解,我将所有的边按照权值进行排序,我必定会先选择最小的边,怎么确定这个边可以用另一数据结构“并查集”,他也是一棵无向树。没有连接这条边肯定要选,所以也有点贪心的意思。最后n-1条边将n个点连接起来就是最小的。
用C++11进行提交,否则CE

#include <bits/stdc++.h>
using namespace std;
const int N = 505, M = 1005;
//定义并查集父亲数组
int fa[N];
//并查集查找祖先函数
int Find(int x)
{
    //相等,已经找到
    if (x == fa[x])
        return x;
    //没有,压缩一下
    return fa[x] = Find(fa[x]);
}
//并查集联合函数
void Union(int x, int y)
{
    //寻找x的祖先
    x = Find(x);
    //寻找y的祖先
    y = Find(y);
    //不一个进行合并
    if (x != y)
        fa[x] = y;
}
//边数组,和用struct数组一样
pair<int, pair<int, int>> V[M];
#define fi first
#define se second
int main()
{
    int n, m;
    cin >> n >> m;
    //并查集父亲数组初始化
    for (int i = 1; i <= n; i++)
        fa[i] = i;
    for (int i = 0, x, y, z; i < m; i++)
    {
        cin >> x >> y >> z;
        V[i] = {z, {x, y}};
    }
    //pair运算符不需要重载
    sort(V, V + m);
    int ans = 0;
    for (int i = 0, x, y, z; i < m; i++)
    {
        //两个点不相连,加入
        if (Find(V[i].se.fi) != Find(V[i].se.se))
        {
            ans += V[i].fi;
            Union(V[i].se.fi, V[i].se.se);
        }
    }
    cout << ans << "\n";
}

2.6178: Alice运快递

第一问是一个很经典的01背包问题,一个物品可以选一次,所以我们这时候的状态是从前一个状态过来的。当前物品是否可以放下,是不是更大都是需要我们考虑的。01背包只能选一次,我们可以优化只用一维dp数组,第二重循环需要倒着进行。
第二问是个很简单的贪心问题,我们可以选择体积最小的货物装上。

#include <bits/stdc++.h>
using namespace std;
int w[1005], c[1005], dp[100005];
int main()
{
    int m, n;
    while (cin >> m >> n)
    {
        for (int i = 0; i < n; i++)
            cin >> w[i] >> c[i];
        //求最大,dp数组清空
        memset(dp, 0, sizeof(dp));
        //循环访问物品
        for (int i = 0; i < n; i++)
        {
             //对dp[i-1][j]查询,看看还能不能放下
             //少一维循环,必须从大到小保证只放一次
             for (int j = m; j >= w[i]; j--)
                dp[j] = max(dp[j], dp[j - w[i]] + c[i]);
        }
           
        sort(w, w + n);
        int ans = 0, sum = 0;
        for (int i = 0; i < n; i++)
        {
            //贪心只取最小
            if (sum + w[i] <= m)
                sum += w[i], ans++;
            else
                break;
        }
        cout << dp[m] << " " << ans << "\n";
    }
    return 0;
}

3.6173: 相同行程查询

这个题目有些困难,我们需要知道这个人的车次,之后我们会找到人然后对应到这个车次。最终我们要查询的是车次,当然我们可以进行排序之后二分。不过这个题目有个更好用的东西,叫map,是stl中的一个。map<key,value>也叫键值对,前面可以把放键,可以理解为就是一个下标,后面放值。所以就是人和车次的map以及车次和人数的map。

#include <bits/stdc++.h>
#define LL long long
using namespace std;
//m为人和车次的键值对
map<string, string> m;
//p为车次和人数的键值对
map<string, int> p;
int main()
{
    int n, a;
    string x, y;
    cin >> n;
    while (n--)
    {
        //读入车次和人数
        cin >> x >> a;
        while (a--)
        {
            //读入人名
            cin >> y;
            //建立人名和车次的键值对
            m[y] = x;
        }
    }
    cin >> n;
    while (n--)
    {
        //读入生病的人
        cin >> y;
        //对生病的车次进行+1操作
        p[m[y]]++;
    }
    cin >> n;
    while (n--)
    {
        cin >> x;
        //直接输出车次上生病的人数
        cout << p[x] << "\n";
    }
    return 0;
}

4.6177: Alice玩汉诺

这个题目我们可以采用常规做法,就是我们递归的时候可以统计移动次数。
当然也可以找到递推式,数量级大的情况就必须使用递推式了。
A->B=(上一次)A->C->B
B->C=(上一次)B->A->C
C->A=(上一次)C->B->A
我这里用了递归写法,我们可以设置变量去统计他们,在每次移动的时候去统计。

#include <bits/stdc++.h>
using namespace std;
int ans[8];
void move(char a, char b)
{
    //进行判断归到不同变量
    if (a == 'A' && b == 'B')
        ans[0]++;
    if (a == 'A' && b == 'C')
        ans[1]++;
    if (a == 'B' && b == 'A')
        ans[2]++;
    if (a == 'B' && b == 'C')
        ans[3]++;
    if (a == 'C' && b == 'A')
        ans[4]++;
    if (a == 'C' && b == 'B')
        ans[5]++;
}
void hanoi(int n, char a, char b, char c)
{
    if (n == 0)
        return;
    hanoi(n - 1, a, c, b);
    //必然把a移动到c
    move(a, c);
    hanoi(n - 1, b, a, c);
}
int main()
{
    int n;
    while (cin >> n)
    {
        //清空ans数组
        memset(ans, 0, sizeof(ans));
        hanoi(n, 'A', 'B', 'C');
        //一次输出答案
        cout << "A->B: " << ans[0] << "\n";
        cout << "A->C: " << ans[1] << "\n";
        cout << "B->A: " << ans[2] << "\n";
        cout << "B->C: " << ans[3] << "\n";
        cout << "C->A: " << ans[4] << "\n";
        cout << "C->B: " << ans[5] << "\n";
    }
    return 0;
}

5.6179: Alice排数字

其实我们可以给其中的8和2拿出来看看有多少个282,排列一定是282828282····
也就是28不断循环的,其中282的个数和2和8均有关,n个2就有n-1个282,m个8就有m个282,两者取小,当然不能是负数。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    while (cin >> s)
    {
        int a[10] = {0};
        for (int i = 0; s[i]; i++)
        {
            //'0'是48,数字下标所对位置++
            a[s[i] - '0']++;
        }
        //2的个数和8的个数取小,有可能编程负数
        cout << max(0, min(a[2] - 1, a[8])) << "\n";
        cout << "Happy Birthday!\n";
    }
    return 0;
}

6.6181: Alice与闪电

这个一个比较复杂的循环题,我们可以将其分两三输出,前m/2行,中间行,后m/2行,前m/2行前面的空格分析下,为m-i个,*为i+1个,中间行全是是n个,然后依次类推。至于中间用空行隔开,我们可以用一个旗子来表示,刚开始没有插旗子,不能输出空行,执行一次就插上了旗子,当然是否要输出空行要在插旗子之前。详见代码flag的操作。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    //定义旗子
    int flag = 0;
    while (cin >> n)
    {
        //插了旗子就要进行换行,第一次不会换行
        if (flag)cout << "\n";
        //插旗子
        flag = 1;
        //找到对应的行
        int m = n / 2;
        for (int i = 0; i < m; i++)
        {
            //输出m-i个空格
            for (int j = m; j > i; j--)
                cout << " ";
            //输出i+1个星号
            for (int j = 0; j <= i; j++)
                cout << "*";
            cout << "\n";
        }
        //输出第m行
        for (int i = 0; i < n; i++)
        {
            cout << "*";
        }
        cout << "\n";
        //倒着输出每一行
        for (int i = m - 1; i >= 0; i--)
        {
            //输出m-1个空格
            for (int j = 0; j < m; j++)
                cout << " ";
            //输出i+1个星号
            for (int j = 0; j <= i; j++)
                cout << "*";
            cout << "\n";
        }
    }
    return 0;
}

7.6180: Alice玩井棋

这是一个比较有趣的游戏,但是不同的思路实现起来难度可能不同,在这里推荐了一个比较简单的实现,
1.横或竖坐标均相同
2.在左上到右下的对角线上
3.在右上到左下对角线上

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int x1, x2, x3, y1, y2, y3;
    while (cin >> x1 >> y1)
    {
        cin >> x2 >> y2;
        cin >> x3 >> y3;
        int f = 0;
        //成行或成列
        if ((x1 == x2 && x2 == x3) || (y1 == y2 && y2 == y3))
            f = 1;
        //从左上到右下
        if (x1 == y1 && x2 == y2 && x3 == y3)
            f = 1;
        //从左下到右上
        if (x1 + y1 == 4 && x2 + y2 == 4 && x3 + y3 == 4)
            f = 1;
        if (f)
            cout << "Win" << endl;
        else
            cout << "Continue" << endl;
    }
    return 0;
}

8.6174: Alice与甜甜圈

空圆柱体的体积,体积为底面积高,
圆环面积为(R*R-r*r)
PI
注意这个题目给我们的数是实数

#include<bits/stdc++.h>
using namespace std;
int main()
{
	//实数包括整数和小数,是double类型的
    double R,r,h;
    double PI = acos(-1);//3.1415926536
    while(cin>>R>>r>>h)
    {
    	//底面积乘高
        double ans = PI*h*(R*R - r*r);
        printf("%.2f\n",ans);
    }
    return 0;
}

9.6175: Alice买口罩

10个口罩一定会浪费掉,所以你买到y个口罩,买x个一次,次数为y/x

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int x,y;
    cin>>x>>y;
    //输出次数
    cout<<y/x;
    return 0;
}

10.6176: 武汉加油!中国加油!

简单C++输出,可以复制粘贴,尽量减少错误

#include <bits/stdc++.h>
using namespace std;
int main()
{
	//记得复制粘贴,不要交错语言
	cout<<"Fighting, Wuhan! Stay strong, China!\n";
}
posted @ 2020-02-15 14:46  暴力都不会的蒟蒻  阅读(765)  评论(0编辑  收藏  举报