天天快樂編程杯中學生信奧聯賽(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";
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM