• 天天快乐编程杯中学生信奥联赛(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 << "
    ";
    }
    

    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 << "
    ";
        }
        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] << "
    ";
        }
        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] << "
    ";
            cout << "A->C: " << ans[1] << "
    ";
            cout << "B->A: " << ans[2] << "
    ";
            cout << "B->C: " << ans[3] << "
    ";
            cout << "C->A: " << ans[4] << "
    ";
            cout << "C->B: " << ans[5] << "
    ";
        }
        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])) << "
    ";
            cout << "Happy Birthday!
    ";
        }
        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 << "
    ";
            //插旗子
            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 << "
    ";
            }
            //输出第m行
            for (int i = 0; i < n; i++)
            {
                cout << "*";
            }
            cout << "
    ";
            //倒着输出每一行
            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 << "
    ";
            }
        }
        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
    ",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!
    ";
    }
    
  • 相关阅读:
    算法导论2.37答案
    算法导论2.37的算法
    heavy dark读《《暗时间》》
    深入SetOP2函数
    c++标准库都有哪些文件
    c++ sort函数的用法
    深入char转换为int/unsigned int的内部机制分析
    顺序容器之vector
    java的动态代理机制详解
    java.lang.IllegalStateException: Web app root system property already set to different value
  • 原文地址:https://www.cnblogs.com/BobHuang/p/12312129.html
Copyright © 2020-2023  润新知