1. 程式人生 > 實用技巧 >第 02 組 每週小結 (2/3)

第 02 組 每週小結 (2/3)

L1-1 嫑廢話上程式碼 (5分)

Linux 之父 Linus Torvalds 的名言是:“Talk is cheap. Show me the code.”(嫑廢話,上程式碼)。本題就請你直接在螢幕上輸出這句話。

輸入格式:

本題沒有輸入。

輸出格式:

在一行中輸出 Talk is cheap. Show me the code.。

輸入樣例:

輸出樣例:

Talk is cheap. Show me the code.

#include<bits/stdc++.h>
using namespace std;
int main()
{
    puts("Talk is cheap. Show me the code."
); }

L1-2 貓是液體 (5分)

測量一個人的體積是很難的,但貓就不一樣了。因為貓是液體,所以可以很容易地通過測量一個長方體容器的容積來得到容器裡貓的體積。本題就請你完成這個計算。

輸入格式:

輸入在第一行中給出 3 個不超過 100 的正整數,分別對應容器的長、寬、高。

輸出格式:

在一行中輸出貓的體積。

輸入樣例:

23 15 20

輸出樣例:

6900

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int a, b, c;
    scanf("%d%d%d", &
a, &b, &c); printf("%d\n", a * b * c); }

L1-3 洛希極限 (10分)

科幻電影《流浪地球》中一個重要的情節是地球距離木星太近時,大氣開始被木星吸走,而隨著不斷接近地木“剛體洛希極限”,地球面臨被徹底撕碎的危險。但實際上,這個計算是錯誤的。
洛希極限(Roche limit)是一個天體自身的引力與第二個天體造成的潮汐力相等時的距離。當兩個天體的距離少於洛希極限,天體就會傾向碎散,繼而成為第二個天體的環。它以首位計算這個極限的人愛德華·洛希命名。(摘自百度百科)
大天體密度與小天體的密度的比值開 3 次方後,再乘以大天體的半徑以及一個倍數(流體對應的倍數是 2.455,剛體對應的倍數是 1.26),就是洛希極限的值。例如木星與地球的密度比值開 3 次方是 0.622,如果假設地球是流體,那麼洛希極限就是 0.622×2.455=1.52701 倍木星半徑;但地球是剛體,對應的洛希極限是 0.622×1.26=0.78372 倍木星半徑,這個距離比木星半徑小,即只有當地球位於木星內部的時候才會被撕碎,換言之,就是地球不可能被撕碎。

本題就請你判斷一個小天體會不會被一個大天體撕碎。

輸入格式:

輸入在一行中給出 3 個數字,依次為:大天體密度與小天體的密度的比值開 3 次方後計算出的值(≤1)、小天體的屬性(0 表示流體、1 表示剛體)、兩個天體的距離與大天體半徑的比值(>1 但不超過 10)。

輸出格式:

在一行中首先輸出小天體的洛希極限與大天體半徑的比值(輸出小數點後2位);隨後空一格;最後輸出^_^ 如果小天體不會被撕碎,否則輸出 T_T

輸入樣例 1:

0.622 0 1.4

輸出樣例 1:

1.53 T_T

輸入樣例 2:

0.622 1 1.4

輸出樣例 2:

0.78 ^_^

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int b;
    double a,c;
    scanf("%lf%d%lf",&a,&b,&c);
    if(!b)
    {
        double t = a * 2.455;
        if(t<c)
            printf("%.2lf ^_^\n",t);
        else
            printf("%.2lf T_T\n",t);
    }
    else
    {
        double t = a * 1.26;
        if(t<c)
            printf("%.2lf ^_^\n",t);
        else
            printf("%.2lf T_T\n",t);
    }
    return 0;
}

L1-4 調和平均 (10分)

N N N 個正數的算數平均是這些數的和除以 N N N,它們的調和平均是它們倒數的算數平均的倒數。本題就請你計算給定的一系列正數的調和平均值。

輸入格式:

每個輸入包含 1 1 1 個測試用例。每個測試用例第 1 1 1 行給出正整數 N ( ≤ 1000 ) N (≤1000) N(1000);第 2 2 2 行給出 N N N 個正數,都在區間 [ 0.1 , 100 ] [0.1,100] [0.1,100] 內。

輸出格式:

在一行中輸出給定數列的調和平均值,輸出小數點後 2 2 2位。

輸入樣例:

8
10 15 12.7 0.3 4 13 1 15.6

輸出樣例:

1.61

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    scanf("%d",&n);
    double sum = 0.0;
    for(int i=0;i<n;i++)
    {
        double x;
        scanf("%lf",&x);
        sum += 1.0/x;
    }
    sum /= 1.0*n;
    sum = 1.0 / sum;
    printf("%.2lf\n",sum);
    return 0;
}

L1-5 胎壓監測 (15分)

小轎車中有一個系統隨時監測四個車輪的胎壓,如果四輪胎壓不是很平衡,則可能對行車造成嚴重的影響。
讓我們把四個車輪 —— 左前輪、右前輪、右後輪、左後輪 —— 順次編號為 1、2、3、4。本題就請你編寫一個監測程式,隨時監測四輪的胎壓,並給出正確的報警資訊。報警規則如下:
如果所有輪胎的壓力值與它們中的最大值誤差在一個給定閾值內,並且都不低於系統設定的最低報警胎壓,則說明情況正常,不報警;
如果存在一個輪胎的壓力值與它們中的最大值誤差超過了閾值,或者低於系統設定的最低報警胎壓,則不僅要報警,而且要給出可能漏氣的輪胎的準確位置;
如果存在兩個或兩個以上輪胎的壓力值與它們中的最大值誤差超過了閾值,或者低於系統設定的最低報警胎壓,則報警要求檢查所有輪胎。

輸入格式:

輸入在一行中給出 6 6 6 [ 0 , 400 ] [0, 400] [0,400] 範圍內的整數,依次為 1 ∼ 4 1 \sim 4 14 號輪胎的胎壓、最低報警胎壓、以及胎壓差的閾值。

輸出格式:

根據輸入的胎壓值給出對應資訊:

  • 如果不用報警,輸出 Normal
  • 如果有一個輪胎需要報警,輸出 Warning: please check #X!,其中 X 是出問題的輪胎的編號;
  • 如果需要檢查所有輪胎,輸出 Warning: please check all the tires!

輸入樣例 1:

242 251 231 248 230 20

輸出樣例 1:

Normal

輸入樣例 2:

242 251 232 248 230 10

輸出樣例 2:

Warning: please check #3!

輸入樣例 3:

240 251 232 248 240 10

輸出樣例 3:

Warning: please check all the tires!

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int a[4], b, c;
    scanf("%d%d%d%d%d%d", &a[0], &a[1], &a[2], &a[3], &b, &c);
    int mx = max(max(a[0], a[1]), max(a[2], a[3]));
    int n1 = 0, n2 = 0, id = -1;
    for (int i = 0; i < 4; i++)
    {
        if (mx - a[i] > c)
            n2++, id = i + 1;
        if (a[i] < b)
            n1++, id = i + 1;
    }
    if (n1 >= 2 || n2 >= 2)
        puts("Warning: please check all the tires!");
    else if (n1 == 1 || n2 == 1)
        printf("Warning: please check #%d!\n", id);
    else if (!n1 && !n2)
        puts("Normal");
    return 0;
}

L1-05 吃火鍋 (15分)

以上圖片來自微信朋友圈:這種天氣你有什麼破事打電話給我基本沒用。但是如果你說“吃火鍋”,那就厲害了,我們的故事就開始了。
本題要求你實現一個程式,自動檢查你朋友給你發來的資訊裡有沒有 chi1 huo3 guo1

輸入格式:

輸入每行給出一句不超過 80 80 80 個字元的、以回車結尾的朋友資訊,資訊為非空字串,僅包括字母、數字、空格、可見的半形標點符號。當讀到某一行只有一個英文句點 . 時,輸入結束,此行不算在朋友資訊裡。

輸出格式:

首先在一行中輸出朋友資訊的總條數。然後對朋友的每一行資訊,檢查其中是否包含 chi1 huo3 guo1,並且統計這樣厲害的資訊有多少條。在第二行中首先輸出第一次出現 chi1 huo3 guo1 的資訊是第幾條(從 1 1 1 開始計數),然後輸出這類資訊的總條數,其間以一個空格分隔。題目保證輸出的所有數字不超過 100 100 100

如果朋友從頭到尾都沒提 chi1 huo3 guo1 這個關鍵詞,則在第二行輸出一個表情 -_-#

輸入樣例 1:

Hello!
are you there?
wantta chi1 huo3 guo1?
that’s so li hai le
our story begins from chi1 huo3 guo1 le
.

輸出樣例 1:

5
3 2

輸入樣例 2:

Hello!
are you there?
wantta qi huo3 guo1 chi1huo3guo1?
that’s so li hai le
our story begins from ci1 huo4 guo2 le
.

輸出樣例 2:

5
-_-#

#include <bits/stdc++.h>
using namespace std;
char s[10005];
int main()
{
    int cnt = 0, ans = 0, id = 0;
    while (cin.getline(s,10005))
    {
        if (strcmp(s, ".") == 0)
            break;
        cnt++;
        if (strstr(s, "chi1 huo3 guo1"))
        {
            ans++;
            if (!id)
                id = cnt;
        }
    }
    printf("%d\n", cnt);
    if (!ans)
        puts("-_-#");
    else
        printf("%d %d\n", id, ans);
    return 0;
}

L1-6 前世檔案 (20分)

網路世界中時常會遇到這類滑稽的算命小程式,實現原理很簡單,隨便設計幾個問題,根據玩家對每個問題的回答選擇一條判斷樹中的路徑(如下圖所示),結論就是路徑終點對應的那個結點。
在這裡插入圖片描述
現在我們把結論從左到右順序編號,編號從 1 開始。這裡假設回答都是簡單的“是”或“否”,又假設回答“是”對應向左的路徑,回答“否”對應向右的路徑。給定玩家的一系列回答,請你返回其得到的結論的編號。

輸入格式:

輸入第一行給出兩個正整數: N ( ≤ 30 ) N(≤30) N30為玩家做一次測試要回答的問題數量; M ( ≤ 100 ) M(≤100) M100為玩家人數。

隨後 M M M 行,每行順次給出玩家的 N N N 個回答。這裡用 y 代表“是”,用 n 代表“否”。

輸出格式:

對每個玩家,在一行中輸出其對應的結論的編號。

輸入樣例:

3 4
yny
nyy
nyn
yyn

輸出樣例:

3
5
6
2

#include <bits/stdc++.h>
using namespace std;
int n, m;
string s;
int find(int ans, int id)
{
    if (id == n)
        return ans;
    if (s[n - id - 1] == 'y')
        return find(ans, id + 1);
    else
        return find(ans + (1 << id), id + 1);
}
int main()
{
    scanf("%d%d", &n, &m);
    while (m--)
    {
        cin >> s;
        cout << find(1, 0) << "\n";
    }
    return 0;
}

L1-7 刮刮彩票 (20分)

“刮刮彩票”是一款網路遊戲裡面的一個小遊戲。如圖所示:
每次遊戲玩家會拿到一張彩票,上面會有 9 9 9 個數字,分別為數字 1 1 1 到數字 9 9 9,數字各不重複,並以 3 × 3 3×3 3×3 的“九宮格”形式排布在彩票上。

在遊戲開始時能看見一個位置上的數字,其他位置上的數字均不可見。你可以選擇三個位置的數字刮開,這樣玩家就能看見四個位置上的數字了。最後玩家再從 3 3 3 橫、 3 3 3 豎、 2 2 2 斜共 8 8 8 個方向中挑選一個方向,方向上三個數字的和可根據下列表格進行兌獎,獲得對應數額的金幣。

數字合計獲得金幣數字合計獲得金幣
6100001672
73617180
872018119
93601936
108020306
11252211080
1210822144
1372231800
1454243600
15180

現在請你寫出一個模擬程式,模擬玩家的遊戲過程。

輸入格式:

輸入第一部分給出一張合法的彩票,即用 3 3 3 3 3 3 列給出 0 0 0 9 9 9 的數字。$0 $表示的是這個位置上的數字初始時就能看見了,而不是彩票上的數字為 0 0 0

第二部給出玩家刮開的三個位置,分為三行,每行按格式 x y 給出玩家刮開的位置的行號和列號(題目中定義左上角的位置為第 1 1 1 行、第 1 1 1 列。)。資料保證玩家不會重複刮開已刮開的數字。

最後一部分給出玩家選擇的方向,即一個整數: 1 1 1 3 3 3 表示選擇橫向的第一行、第二行、第三行, 4 4 4 6 6 6表示縱向的第一列、第二列、第三列, 7 7 7 8 8 8分別表示左上到右下的主對角線和右上到左下的副對角線。

輸出格式:

對於每一個刮開的操作,在一行中輸出玩家能看到的數字。最後對於選擇的方向,在一行中輸出玩家獲得的金幣數量。

輸入樣例:

1 2 3
4 5 6
7 8 0
1 1
2 2
2 3
7

輸出樣例:

1
5
6
180

#include <bits/stdc++.h>
using namespace std;
int mp[4][4];
int val[] = {10000, 36, 720, 360, 80, 252, 108, 72, 54, 180, 72, 180, 119, 36, 306, 1080, 144, 1800, 3600};
int main()
{
    int sum = 45;
    int x1 = -1, y1 = -1;
    for (int i = 1; i <= 3; i++)
        for (int j = 1; j <= 3; j++)
        {
            scanf("%d", &mp[i][j]);
            sum -= mp[i][j];
            if (!mp[i][j])
                x1 = i, y1 = j;
        }
    mp[x1][y1] = sum;
    for (int i = 0; i < 3; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        printf("%d\n", mp[x][y]);
    }
    int op, ans = 0;
    scanf("%d", &op);
    for (int i = 1; i <= 3; i++)
    {
        if (op >= 1 && op <= 3)
            ans += mp[op][i];
        else if (op >= 4 && op <= 6)
            ans += mp[i][op - 3];
        else if (op == 7)
            ans += mp[i][i];
        else
            ans += mp[i][4 - i];
    }
    printf("%d\n", val[ans - 6]);
    return 0;
}

L2-8 簡單計算器 (25分)

本題要求你為初學資料結構的小夥伴設計一款簡單的利用堆疊執行的計算器。如上圖所示,計算器由兩個堆疊組成,一個堆疊 S 1 S_1 S1存放數字,另一個堆疊 S 2 S_2 S2存放運算子。計算器的最下方有一個等號鍵,每次按下這個鍵,計算器就執行以下操作:

  1. S 1 S_1 S1中彈出兩個數字,順序為 n 1 n_1 n1 n 2 n_2 n2
  2. S 2 S_2 S2中彈出一個運算子 o p op op
  3. 執行計算 n 2 o p n 1 n_2 \ op \ n_1 n2opn1
  4. 將得到的結果壓回 S 1 S_1 S1

直到兩個堆疊都為空時,計算結束,最後的結果將顯示在螢幕上。

輸入格式:

輸入首先在第一行給出正整數 N ( 1 < N ≤ 1 0 3 ) N(1<N≤10^3) N1<N103,為 S ​ 1 S​_1 S1中數字的個數。

第二行給出 N N N 個絕對值不超過 100 100 100 的整數;第三行給出 N − 1 N−1 N1 個運算子 —— 這裡僅考慮 +-*/ 這四種運算。一行中的數字和符號都以空格分隔。

輸出格式:

將輸入的數字和運算子按給定順序分別壓入堆疊 S 1 S_1 S1 S 2 S_2 S2​​ ,將執行計算的最後結果輸出。注意所有的計算都只取結果的整數部分。題目保證計算的中間和最後結果的絕對值都不超過 1 0 9 10^9 109​​ 。

如果執行除法時出現分母為零的非法操作,則在一行中輸出:ERROR: X/0,其中 X是當時的分子。然後結束程式。

輸入樣例 1:

5
40 5 8 3 2
/ * - +

輸出樣例 1:

2

輸入樣例 2:

5
2 5 8 4 4

  • / - +

輸出樣例 2:

ERROR: 5/0

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    cin >> n;
    stack<int> s1;
    stack<char> s2;
    for (int i = 0, x; i < n; i++)
        cin >> x, s1.push(x);
    for (int i = 1; i < n; i++)
    {
        char x;
        cin >> x;
        s2.push(x);
    }
    int flag = 1;
    while (!s2.empty())
    {
        int n1 = s1.top();
        s1.pop();
        int n2 = s1.top();
        s1.pop();
        char x = s2.top();
        s2.pop();
        if (x == '+')
            s1.push(n1 + n2);
        else if (x == '-')
            s1.push(n2 - n1);
        else if (x == '*')
            s1.push(n1 * n2);
        else
        {
            if (n1 == 0)
            {
                flag = 0;
                printf("ERROR: %d/0\n", n2);
                break;
            }
            else
                s1.push(n2 / n1);
        }
    }
    if (flag)
        printf("%d\n", s1.top());
    return 0;
}

L2-9 口罩發放 (25分)

為了抗擊來勢洶洶的 COVID19 新型冠狀病毒,全國各地均啟動了各項措施控制疫情發展,其中一個重要的環節是口罩的發放。

某市出於給市民發放口罩的需要,推出了一款小程式讓市民填寫資訊,方便工作的開展。小程式收集了各種資訊,包括市民的姓名、身份證、身體情況、提交時間等,但因為資料量太大,需要根據一定規則進行篩選和處理,請你編寫程式,按照給定規則輸出口罩的寄送名單。

輸入格式:

輸入第一行是兩個正整數 D D D P ( 1 ≤ D , P ≤ 30 ) P(1≤D,P≤30) P1D,P30,表示有 D D D 天的資料,市民兩次獲得口罩的時間至少需要間隔 P P P 天。

接下來 D D D 塊資料,每塊給出一天的申請資訊。第 i i i 塊資料 ( i = 1 , ⋯ , D ) (i=1,⋯,D) i=1,,D的第一行是兩個整數 T i T_i Ti S ​ i ( 1 ≤ T i , S ​ i ​ ​ ≤ 1000 S_​i(1≤T_{i}, S_​{i}​​ ≤1000 Si1Ti,Si1000,表示在第 i i i天有 T i T_i Ti條申請,總共有 S i S_i Si個口罩發放名額。隨後 T ​ i T_​{i} Ti行,每行給出一條申請資訊,格式如下:

姓名 身份證號 身體情況 提交時間

給定資料約束如下:

  • 姓名 是一個長度不超過 10 10 10 的不包含空格的非空字串;
  • 身份證號 是一個長度不超過 20 20 20 的非空字串;
  • 身體情況 0 0 0 或者 1 1 1 0 0 0 表示自覺良好, 1 1 1 表示有相關症狀;
  • 提交時間 是 hh:mm,為 24 24 24小時時間(由 00:0023:59。例如 09:08。)。注意,給定的記錄的提交時間不一定有序;
  • 身份證號 各不相同,同一個身份證號被認為是同一個人,資料保證同一個身份證號姓名是相同的。

能發放口罩的記錄要求如下:

  • 身份證號 必須是 18 18 18 位的數字(可以包含前導0);
  • 同一個身份證號若在第 i i i 天申請成功,則接下來的 P P P 天不能再次申請。也就是說,若第 i i i 天申請成功,則等到第 i + P + 1 i+P+1 i+P+1天才能再次申請;
  • 在上面兩條都符合的情況下,按照提交時間的先後順序發放,直至全部記錄處理完畢或 S i S_i Si個名額用完。如果提交時間相同,則按照在列表中出現的先後順序決定。

輸出格式:

對於每一天的申請記錄,每行輸出一位得到口罩的人的姓名及身份證號,用一個空格隔開。順序按照發放順序確定。

在輸出完發放記錄後,你還需要輸出有合法記錄的、身體狀況為 1 1 1 的申請人的姓名及身份證號,用空格隔開。順序按照申請記錄中出現的順序確定,同一個人只需要輸出一次。

輸入樣例:

4 2
5 3
A 123456789012345670 1 13:58
B 123456789012345671 0 13:58
C 12345678901234567 0 13:22
D 123456789012345672 0 03:24
C 123456789012345673 0 13:59
4 3
A 123456789012345670 1 13:58
E 123456789012345674 0 13:59
C 123456789012345673 0 13:59
F F 0 14:00
1 3
E 123456789012345674 1 13:58
1 1
A 123456789012345670 0 14:11

輸出樣例:

D 123456789012345672
A 123456789012345670
B 123456789012345671
E 123456789012345674
C 123456789012345673
A 123456789012345670
A 123456789012345670
E 123456789012345674

樣例解釋:

輸出中,第一行到第三行是第一天的部分;第四、五行是第二天的部分;第三天沒有符合要求的市民;第六行是第四天的部分。最後兩行按照出現順序輸出了可能存在身體不適的人員。

#include <bits/stdc++.h>
using namespace std;
map<string, int> mp;
set<string> st;
vector<pair<string, string>> ans;
struct Node
{
    string name, id;
    int time, idx;
    Node() {}
    Node(string n0, string i0, int t0, int i1) : name(n0), id(i0), time(t0), idx(i1) {}
    bool operator<(const Node &C) const
    {
        if (time == C.time)
            return idx < C.idx;
        return time < C.time;
    }
} node[1005];
int main()
{
    int d, p;
    scanf("%d%d", &d, &p);
    for (int _ = 1; _ <= d; _++)
    {
        int t, s, cnt = 0;
        scanf("%d%d", &t, &s);
        for (int i = 1; i <= t; i++)
        {
            string name, id;
            int state, h, m;
            cin >> name >> id >> state;
            scanf("%d:%d", &h, &m);
            int flag = 1;
            for (int j = 0; j < id.size(); j++)
                if (!isdigit(id[j]))
                {
                    flag = 0;
                    break;
                }
            if (id.length() == 18 && flag)
            {
                node[cnt++] = {name, id, h * 60 + m, i};
                if (state == 1 && st.count(id) == 0)
                {
                    ans.push_back({name, id});
                    st.insert(id);
                }
            }
        }
        sort(node, node + cnt);
        for (int i = 0, j = 0; i < cnt && j < s; i++)
        {
            if (!mp[node[i].id] || _ - mp[node[i].id] > p)
            {
                cout << node[i].name << " " << node[i].id << "\n";
                j++;
                mp[node[i].id] = _;
            }
        }
    }
    for (auto i : ans)
        cout << i.first << " " << i.second << "\n";
    return 0;
}

L2-10 完全二叉樹的層序遍歷 (25分)

一個二叉樹,如果每一個層的結點數都達到最大值,則這個二叉樹就是完美二叉樹。對於深度為 D D D 的,有 N N N 個結點的二叉樹,若其結點對應於相同深度完美二叉樹的層序遍歷的前 N N N 個結點,這樣的樹就是完全二叉樹。

給定一棵完全二叉樹的後序遍歷,請你給出這棵樹的層序遍歷結果。

輸入格式:

輸入在第一行中給出正整數 N ( ≤ 30 ) N(≤30) N30,即樹中結點個數。第二行給出後序遍歷序列,為 N N N 個不超過 100 100 100 的正整數。同一行中所有數字都以空格分隔。

輸出格式:

在一行中輸出該樹的層序遍歷序列。所有數字都以 1 1 1 個空格分隔,行首尾不得有多餘空格。

輸入樣例:

8
91 71 2 34 10 15 55 18

輸出樣例:

18 34 55 71 2 10 15 91

#include <bits/stdc++.h>
using namespace std;
int a[105], ans[105], n, cnt = 1;
void build(int rt)
{
    if (rt > n)
        return;
    build(rt << 1);
    build(rt << 1 | 1);
    ans[rt] = a[cnt++];
}
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    build(1);
    for (int i = 1; i <= n; i++)
        printf("%d%c", ans[i], (i == n) ? '\n' : ' ');
    return 0;
}

L2-11 網紅點打卡攻略 (25分)

一個旅遊景點,如果被帶火了的話,就被稱為“網紅點”。大家來網紅點遊玩,俗稱“打卡”。在各個網紅點打卡的快(省)樂(錢)方法稱為“攻略”。你的任務就是從一大堆攻略中,找出那個能在每個網紅點打卡僅一次、並且路上花費最少的攻略。

輸入格式:

首先第一行給出兩個正整數:網紅點的個數 N ( 1 < N ≤ 200 ) N(1<N≤200) N1<N200和網紅點之間通路的條數 M M M。隨後 M M M 行,每行給出有通路的兩個網紅點、以及這條路上的旅行花費(為正整數),格式為“網紅點 1 1 1 網紅點2 費用”,其中網紅點從 1 1 1 N N N 編號;同時也給出你家到某些網紅點的花費,格式相同,其中你家的編號固定為 0

再下一行給出一個正整數 K K K,是待檢驗的攻略的數量。隨後 K K K 行,每行給出一條待檢攻略,格式為:
n V 1 V 2 ⋯ V n n \ V_{1} \ V_{2}\ ⋯ \ V_{n} nV1V2Vn
​​ 其中 n ( ≤ 200 ) n(≤200) n(200) 是攻略中的網紅點數, V ​ i V​_i Vi是路徑上的網紅點編號。這裡假設你從家裡出發,從 V 1 V_1 V1開始打卡,最後從 V n V_n Vn回家。

輸出格式:

在第一行輸出滿足要求的攻略的個數。
在第二行中,首先輸出那個能在每個網紅點打卡僅一次、並且路上花費最少的攻略的序號(從 1 1 1 開始),然後輸出這個攻略的總路費,其間以一個空格分隔。如果這樣的攻略不唯一,則輸出序號最小的那個。
題目保證至少存在一個有效攻略,並且總路費不超過 1 0 9 10^9 109​ 。

輸入樣例:

6 13
0 5 2
6 2 2
6 0 1
3 4 2
1 5 2
2 5 1
3 1 1
4 1 2
1 6 1
6 3 2
1 2 1
4 5 3
2 0 2
7
6 5 1 4 3 6 2
6 5 2 1 6 3 4
8 6 2 1 6 3 4 5 2
3 2 1 5
6 6 1 3 4 5 2
7 6 2 1 3 4 5 2
6 5 2 1 4 3 6

輸出樣例:

3
5 11

樣例說明:

第 2、3、4、6 條都不滿足攻略的基本要求,即不能做到從家裡出發,在每個網紅點打卡僅一次,且能回到家裡。所以滿足條件的攻略有 3 條。

第 1 條攻略的總路費是:(0->5) 2 + (5->1) 2 + (1->4) 2 + (4->3) 2 + (3->6) 2 + (6->2) 2 + (2->0) 2 = 14;

第 5 條攻略的總路費同理可算得:1 + 1 + 1 + 2 + 3 + 1 + 2 = 11,是一條更省錢的攻略;

第 7 條攻略的總路費同理可算得:2 + 1 + 1 + 2 + 2 + 2 + 1 = 11,與第 5 條花費相同,但序號較大,所以不輸出。

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int mp[1005][1005], a[1005];
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= n; j++)
        {
            if (i == j)
                mp[i][j] = 0;
            else
                mp[i][j] = INF;
        }
    for (int i = 0; i < m; i++)
    {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        mp[a][b] = mp[b][a] = w;
    }
    int k;
    scanf("%d", &k);
    int cnt = 0, ans = INF, id = -1;
    for (int _ = 1; _ <= k; _++)
    {
        set<int> vis;
        int num;
        scanf("%d", &num);
        for (int i = 0; i < num; i++)
            scanf("%d", &a[i]);
        a[num] = 0;
        if (num < n)
            continue;
        int last = 0, sum = 0, flag = 1;
        for (int i = 0; i <= num; i++)
        {
            int n1 = vis.size();
            vis.insert(a[i]);
            if (n1 == vis.size() || mp[last][a[i]] == INF)
            {
                flag = 0;
                break;
            }
            sum += mp[last][a[i]];
            last = a[i];
        }
        if (flag && vis.size() == n + 1)
        {
            cnt++;
            if (sum < ans)
            {
                ans = sum;
                id = _;
            }
        }
    }
    printf("%d\n", cnt);
    printf("%d %d\n", id, ans);
    return 0;
}

L3-12 那就別擔心了 (30分)

下圖轉自“英式沒品笑話百科”的新浪微博 —— 所以無論有沒有遇到難題,其實都不用擔心。

博主將這種邏輯推演稱為“邏輯自洽”,即從某個命題出發的所有推理路徑都會將結論引導到同一個最終命題(開玩笑的,千萬別以為這是真正的邏輯自洽的定義……)。現給定一個更為複雜的邏輯推理圖,本題就請你檢查從一個給定命題到另一個命題的推理是否是“邏輯自洽”的,以及存在多少種不同的推理路徑。例如上圖,從“你遇到難題了嗎?”到“那就別擔心了”就是一種“邏輯自洽”的推理,一共有 3 條不同的推理路徑。

輸入格式:

輸入首先在一行中給出兩個正整數 N ( 1 < N ≤ 500 ) N(1<N≤500) N1<N500 M M M,分別為命題個數和推理個數。這裡我們假設命題從 1 1 1 N N N 編號。

接下來 M M M 行,每行給出一對命題之間的推理關係,即兩個命題的編號 S1 S2,表示可以從 S1 推出 S2。題目保證任意兩命題之間只存在最多一種推理關係,且任一命題不能迴圈自證(即從該命題出發推出該命題自己)。

最後一行給出待檢驗的兩個命題的編號 A B

輸出格式:

在一行中首先輸出從 AB 有多少種不同的推理路徑,然後輸出 Yes 如果推理是“邏輯自洽”的,或 No 如果不是。
題目保證輸出資料不超過 10 ​ 9 10​^9 109

輸入樣例 1:

7 8
7 6
7 4
6 5
4 1
5 2
5 3
2 1
3 1
7 1

輸出樣例 1:

3 Yes

輸入樣例 2:

7 8
7 6
7 4
6 5
4 1
5 2
5 3
6 1
3 1
7 1

輸出樣例 2:

3 No

#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
const int maxm = 5e5 + 5;
struct edge
{
    int to, nxt;
} E[maxm];
int head[maxn], dp[maxn], cnt, st, ed, flag;
void addedge(int u, int v)
{
    E[cnt].to = v;
    E[cnt].nxt = head[u];
    head[u] = cnt++;
}
void init()
{
    memset(head, -1, sizeof(head));
    memset(dp, -1, sizeof(dp));
    cnt = 0;
    flag = 1;
}
int dfs(int u)
{
    if (dp[u] != -1)
        return dp[u];
    int sum = 0;
    for (int i = head[u]; ~i; i = E[i].nxt)
    {
        int v = E[i].to;
        sum += dfs(v);
    }
    if (sum == 0)
        flag = 0;
    return dp[u] = sum;
}
int main()
{
    init();
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        addedge(x, y);
    }
    scanf("%d%d", &st, &ed);
    dp[ed] = 1;
    printf("%d ", dfs(st));
    puts(flag ? "Yes" : "No");
    return 0;
}