針對我比較不熟的題目做的筆記
這樣下次考試前就不用翻程式碼進入回憶錄了

公式題

UVA 10021 Satellites

題意

計算兩個在相同軌道的衛星的距離

a:衛星到地球的距離
b:與地球形成的夾角
s:角度的單位

輸出:兩個衛星之間的弧長、弦長

思路

1 deg = 60 min
所以如果角度單位是 min,先幫將其換成 deg
如果角度大於 180 度的話,使用補角
衛星的軌道半徑為 (6440 是地球的半徑)
將度轉換成弧度
弧長公式:
弦長公式:

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <bits/stdc++.h>
using namespace std;

int main() {
    double a, b, r;
    string s;
    while (cin >> a >> b >> s) {
        r = 6440 + a;
        if (s == "min") b /= 60;
        if (b > 180) b = 360 - b;
        b = b * acos(-1) / 180; 
        cout << fixed << setprecision(6) << r*b << ' ' << 2*r*sin(b/2) << endl;
    }
}

UVA 10056 What is the Probability?

題意

總共有 n 個人,輪流擲骰子直到有人獲勝,獲勝條件隨機
給你達成獲勝條件的機率,計算第 r 個人獲勝的機率

思路

總人數:n
獲勝機率:p
計算勝率的目標:r
單次失敗的機率:q = 1 - p

如果獲勝機率 p 為 0,則所有人都不可能獲勝
因此做一個特判,直接輸出 0

直接帶入公式後輸出:

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <bits/stdc++.h>
using namespace std;

int main() {
    int t;
    for (cin >> t; t; t--) {
        double n, p, r, q;
        cin >> n >> p >> r;
        q = 1-p;
        if (!p) cout << p;
        else cout << fixed << setprecision(4) << pow(q, (r-1)) * p / (1 - pow(q, n));
        cout << endl;
    }
}

UVA 10642 Can You Solve It?

題意

依照題目的附圖中提供的 x , y 軸和移動路線
從座標 (st, sy) 到 (ex, ey) 需要移動多少次

思路

對於 (x, y) 點在圖上的斜線可以給予一個代號稱呼為 k = x + y
例如 (0, 1)k = 1 這條線上,(1, 0) 也在 K = 1 這條線上,(0, 2) 則在 k = 2 這條線上
那要計算從 (0, 0) 走到 (x, y) 需要幾條路,可以用此公式來計算:走到 k 線要幾條路 + 偏移量 x
走到 k 有幾條路的公式:
最後 K = x + y 可以推導出從 (0, 0) 走到 (x, y) 經過幾條路的函數:

題目會給予起點 (sx, sy) 跟終點 (ex, ey)
程式碼只需要讀取資料後並輸出 S(ex, ey) - S(sx, sy) 即可

程式碼

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
using namespace std;

int main() {
    int n, i = 1;
    for (cin >> n; i <= n; i++) {
        int sx, sy, ex, ey;
        cin >> sx >> sy >> ex >> ey;
        cout << "Case " << i << ": " << ((ex+ey)*(ex+ey+1)/2 + ex) - ((sx+sy)*(sx+sy+1)/2 + sx) << endl;
    }
}

UVA 10268 498-bis

題意

給你一個數字 x 跟一串數字 a
a 代表一個一元多次方程式,每一項數字由左到右的常數
例如:1 2 1 代表

目的為輸出將 a 方程式微分後代入 x 的結果

思路

因為方程式的常數微分後為 0,所以先將其移除
計算方式為:第 i 個常數 乘上 當前次方 乘上 x當前次方減 1 的次方
當前次方 = n 常數總數(陣列長度) - i 第幾個數字(陣列索引)
轉換成程式碼為:v[i]*(n-i)*pow(x, n-i-1)
不過使用 pow 計算 x 的次方會有精確度的問題
所以使用每次迴圈將總和乘以 x 一次代替

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define int long long

signed main() {
    string s; int x, n;
    while (cin >> x) {
        cin.ignore();
        getline(cin, s);
        stringstream ss(s);
        vector<int> v;
        while (ss >> n) v.pb(n);
        v.pop_back();

        double sum = 0; n = v.size();
        for (int i = 0; i < n; i++) {
            sum *= x;
            sum += v[i]*(n-i);
        }
        cout << (int)sum << '\n';
    }
}

UVA 10242 Fourth Point!!

題意

題目給予平行四邊形中兩鄰邊的端點
因為其中一點會重複兩次,目的是找出沒提到的第四點座標

思路

平行四邊形中對角座標相加後會相等(不知道原理的人自己去 Google)
可以用其特性來計算第四點
公式為:

程式碼只需要判斷哪一點是重複的,然後帶入公式即可

不會發生同一邊的端點座標相等,所以只需要判斷 4 次

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <bits/stdc++.h>
using namespace std;

int main() {
    double x1, x2, x3, x4, y1, y2, y3, y4;
    while (cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3 >> x4 >> y4) {
        double x, y;
        if (x1 == x3 && y1 == y3) {
            x = x2 + x4 - x1;
            y = y2 + y4 - y1;
        }
        else if (x1 == x4 && y1 == y4) {
            x = x2 + x3 - x1;
            y = y2 + y3 - y1;
        }
        else if (x2 == x4 && y2 == y4) {
            x = x1 + x3 - x2;
            y = y1 + y3 - y2;
        }
        else {
            x = x1 + x4 - x2;
            y = y1 + y4 - y2;
        }
		
        cout << fixed << setprecision(3) << x << ' ' << y << endl;
    }
}

先背下來題意就不用花時間看英文文章題

UVA 10057 A mid-summer night’s dream

題意

每筆測資給你 n 和 n 個數字
將數字放進公式後
輸出:

  1. 使算式能得到最小值的 A 的多種可能中,最小的 A
  2. 輸入的數字有多少個能得到算式的最小值
  3. A 有幾種可能

思路

能使算式的得到最小值的 A,其實就是整個數列的中位數
當 n 是奇數的時候,中位數為第 n/2 個數字
當 n 是偶數的時候,中位數為第 n/2-1 跟第 n/2 個數字相加除二

輸出 1 要判斷 n 是否為奇數或偶數,則輸出第 n/2n/2-1 個數
輸出 2 要判斷 n 是否為奇數或偶數,則輸出第 n/2n/2 + n/2-1 個數出現的次數

n 為偶數時,第 n/2n/2-1 個數值相等時,次數不用重複累加

輸出 3 要判斷 n 是否為奇數或偶數,則輸出第 1 (中位數只有一種可能) 或 n/2 個數減去 n/2-1 個數加 1A ~ B 的有幾個數字為 B - A + 1

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    while (cin >> n) {
        vector<int> v(n);
        map<int, int> mp;
        for (auto &i : v) {
            cin >> i;
            mp[i]++;
        }
        sort(v.begin(), v.end());
		
        if (n&1) cout << v[n/2] << ' ' << mp[v[n/2]] << " 1\n";
        else cout << v[n/2-1] << ' ' << (v[n/2-1] == v[n/2] ? mp[v[n/2]] : mp[v[n/2]] + mp[v[n/2-1]]) << ' ' << v[n/2]-v[n/2-1]+1 << endl;
    }
}

UVA 10415 Eb Alto Saxophone Player

題意

根據題目給予的薩克斯風音階與按鍵
判斷輸入的音階順序,計算每個按鍵被按下的次數

按下的次數 會統計:之前原本沒按下的按鍵被按下
所以如果當前按下的按鍵,是上一個音階已經按下的按鍵,並不會計入次數

思路

使用 map 紀錄 mp[按鍵] = pair<按鍵, vector<範圍>>,範圍 = pair<起點, 終點>
接著從頭開始讀取音階
然後檢查所有按鍵有無在該音階的範圍
如果有先標註為按下,再檢查當前是否已經被按下,有的話計數器++
如果沒有在範圍內,則將按鍵標註為放開
最後輸出計數器的值

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n;
    for (cin >> n; n; n--) {
        int cnt[10] = {}, n[10] = {};
        map<char, vector<pair<int, int>>> mp = {
            {'c', {{1, 3}, {6, 9}}},
            {'d', {{1, 3}, {6, 8}}},
            {'e', {{1, 3}, {6, 7}}},
            {'f', {{1, 3}, {6, 6}}},
            {'g', {{1, 3}}},
            {'a', {{1, 2}}},
            {'b', {{1, 1}}},
            {'C', {{2, 2}}},
            {'D', {{0, 3}, {6, 8}}},
            {'E', {{0, 3}, {6, 7}}},
            {'F', {{0, 3}, {6, 6}}}, 
            {'G', {{0, 3}}},
            {'A', {{0, 2}}},
            {'B', {{0, 1}}}
        };
        string s;
        cin >> s;
        for (auto c : s) {
            for (int i = 0; i < 10; i++) {
                bool f = false;
                for (auto j : mp[c]) if(i >= j.first && i <= j.second) f = true;
				
                if (f) {
                    if (!n[i]) cnt[i]++;
                    n[i] = 1;
                }
                else n[i] = 0;
            }
        }
		
        for (auto &i : cnt) cout << i << " \n"[&i == cnt+9];
    }
}

UVA 118 Mutant Flatworld Explorers

題意

模擬一個機器人按照指令在平台上移動
單純的按照指令移動會使機器人走出平台
所以機器人會記取上一次的教訓,不會在同一個地方失足第二次
請輸出每次指令執行完畢後,輸出機器人的位置及面向方位
如果掉出平台則輸出之前的位置且標記 LOST

思路

指令包含左轉跟右轉和前進
轉方向可以用方向 0 1 2 3 代表 上 右 下 左
左轉可以用 d = (d+3)%4 計算
右轉可以用 d = (d+1)%4 計算
前進只需要邊界判斷,然後用一個二維陣列做失足的紀錄

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, m, nx, ny, d, dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
    string dir = "NESW", s;
    char cd;
    cin >> n >> m;
    vector<vector<int>> v(m+1, vector<int>(n+1, 0));
    while (cin >> nx >> ny >> cd) {
        bool f = false;
        cin >> s;
        d = dir.find(cd);
        for (auto c : s) {
            if (c == 'L') d = (d+3)%4;
            if (c == 'R') d = (d+1)%4;
            if (c == 'F') {
                nx += dx[d]; ny += dy[d];
                if (nx < 0 || nx > n || ny < 0 || ny > m) {
                    nx -= dx[d]; ny -= dy[d];
                    if (!v[ny][nx]) {
                        v[ny][nx] = 1;
                        f = true;
                        break;
                    }
               }
            }
        }

        cout << nx << ' ' << ny << ' ' << dir[d] << (f ? " LOST" : "") << endl;
    }
}

UVA 11005 Cheapest Base

題意

給你 0 ~ 35 的成本跟整數 n
計算 n 是 2 ~ 36 進位的情況下,每一個位數的成本加總
找出成本總和最小的進位(輸出所有可能)

思路

在 i 進位時,n 對 i 取餘數來取得每一位數,取完後將 n 除 i 以刪除位數
可以使用 while 迴圈來取位數至數字歸零
接著透過成本表計算出成本總和後記錄並標註最小值
最後輸出成本總和最小的進位數

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, t;
    for (cin >> n, t = 0; n; n--) {
        if (t) cout << endl;
        cout << "Case " << ++t << ":\n";
        vector<int> v(36);
        for (auto &i : v) cin >> i;
		
        int k;
        for (cin >> k; k; k--) {
            int input, tmp, mn = 1e9;
            vector<int> ans(38, 0);
            cin >> input;
			
            for (int i = 2; i <= 36; i++) {
                int cost = 0;
                tmp = input;
                while (tmp) {
                    cost += v[tmp % i];
                    tmp /= i;
                }
                mn = min(mn, cost);
                ans[i] = cost;
            }
            cout << "Cheapest base(s) for number " << input << ":";
            for (int i = 2; i <= 36; i++) if (ans[i] == mn) cout << ' ' << i;
            cout << endl;
        }
    }
}

UVA 10093 An Easy Problem!

題意

給你一個 N 進位的數字 X(要用字串存)
且 X 可以被 N - 1 整除
請判斷最小的 N 為何並輸出

思路

如果這個數字 X11N 有可能是 2、3、4、5
但是可以透過兩個限制來找出 N

  1. N - 1 要可以整除 X
  2. 如果 XN 進位,X 的每一個位數必須小於 N

所以先遍歷一次 X,找出最大的位數
N = 最大的位數 + 1 開始到 62 進行大數除法,如果可以整除就輸出

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;

int main() {
    string s, f = "0123456789ABCDEFGHIJKMNOLPQRSTUVWXYZabcdefghijkmnolpqrstuvwxyz";
    while (cin >> s) {
        int sum = 0, mx = 2;
        for (auto &c : s) if (isalnum(c)) mx = max(mx, (int)f.find(c)+1);
		
        string ans = "such number is impossible!";
        for (int i = mx; i <= 62; i++) {
            int sum = 0;
            for (auto &c : s) if (isalnum(c)) sum = (sum*i+f.find(c))%(i-1);
            if (!sum) {
                ans = to_string(i);
                break;
            }
        }
        cout << ans << endl;
    }
}

UVA 11321 Sort! Sort!! And Sort!!!

題意

按照特地條件進行排序
若此項條件兩者相同,則使用下一項條件

  • 兩數除以 m 的餘數大小
  • 奇數在前偶數在後
  • 若原為奇數較大者在前,較小者在後。若原為偶數則較小者在前,較大者在後。

思路

如果兩者除以 m 的餘數可以比出大小(不相同),就回傳餘數比大小
如果兩者都是奇數,就回傳原數值 大在前
如果兩者都是偶數,就回傳原數值 小在前
若上述條件都不符合,代表兩者一奇一偶,回傳 a 是否為奇數(不成立代表 b 是奇數)

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <bits/stdc++.h>
using namespace std;

int n, m;

bool cmp(int a, int b) {
    if (a%m != b%m) return a%m < b%m;
    if (a&1 && b&1) return a > b;
    if (!(a&1) && !(b&1)) return a < b;
    return a&1;
}

int main() {
    while (cin >> n >> m, cout << n << ' ' << m << endl, n+m) {
        vector<int> v(n);
        for (auto &i : v) cin >> i;
        sort(v.begin(), v.end(), cmp);
        for (auto i : v) cout << i << endl;
    }
}

UVA 10101 Bangla Numbers

題意

將輸入的數字依照孟加拉的單位輸出

思路

因為題目的測資範圍限制,kuti 只有機會出現一次
可以寫一個遞迴函式,該函式可以換算小於 kuti 的數字
如果數字大於 kuti,就把前後分兩部分拿去換算

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <bits/stdc++.h>
using namespace std;
#define int long long

vector<pair<int, string>> v = {
    {100000, " lakh"},
    {1000, " hajar"},
    {100, " shata"},
};

int bn(int x) {
    if (x >= 10000000) {
        bn(x / 10000000);
        cout << " kuti";
        x %= 10000000;
    }
    for (auto i : v) {
        if (x >= i.first) {
            cout << ' ' << x/i.first << i.second;
            x %= i.first;
        }
    }
    if (x > 0) cout << ' ' << x;
}

signed main() {
    int n, j = 0;
    while (cin >> n) {
        cout << setw(4) << ++j << '.';
        if (!n) cout << " 0";
        else bn(n);
        cout << endl;
    }
}

UVA 10050 Hartals

題意

給你每個罷工團體罷工的頻率,代表從第一天開始數,每隔幾天後會罷工
計算 N 天中(從 1 開始數),有多少天有罷工事件發生

星期五、六為每週假日,假日罷工不計入統計

思路

用一個陣列儲存罷工頻率
用迴圈模擬 1 ~ N 天,判斷天數能不能被頻率整除,可以的話計數器++
如果天數對 7 取餘數為 6、0,代表當天為週五和週六,則跳過
輸出計數器

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <vector>
using namespace std;

int main() {
    int t;
    for (cin >> t; t; t--) {
        int n, p, cnt = 0;
        cin >> n >> p;
        vector<int> v(p);
        for (auto &i : v) cin >> i;
        
        for (int i = 1; i <= n; i++) {
            if (i%7 == 6 || i%7 == 0) continue;
            for (auto d : v) if (!(i%d)) {
                cnt++;
                break;
            }
        }
        
        cout << cnt << endl;
    }
}