牛客小白月賽33全題解


小白月賽33題解(全)

A.字符統計

題目鏈接:https://ac.nowcoder.com/acm/contest/11210/A

直接讀入字符串進行遍歷即可。用getline()讀一行字符串,注意:cin讀入后的回車會留在輸入緩沖區,getline()的不會,所以在使用cin后,getline()之前需要清空輸入緩沖區

注意行開頭空格和空行的特殊處理。

AC代碼:

#include <iostream>

using namespace std;

string s;
int r, dc, zf;

int main()
{
    int t;
    cin >> t;
    getline(cin, s);
    
    while(t--)
    {
        r = dc = zf = 0;
        while(getline(cin, s) && s != "=====")
        {
            r++;
            for(int i = 0; i < s.size(); i++)
            {
                if(s[i] == ' ' && i) dc++;
                zf++;
            }
            if(s.size())dc++;
        }
        cout << r << ' ' << dc << ' ' << zf << endl;
    }
    
    return 0;
}

B.連分數

題目鏈接:https://ac.nowcoder.com/acm/contest/11210/B

對一般的\(p/q\)\(p / q = a + 1 / m, 其中m = q / (p \% q)\),然后令分子 = q, 分母 = p % q,繼續上述操作,直到$ p % q == 0$時結束操作。

細節處理:

  1. 沒輸出一個a,如果p、q更新后可以進行操作,則輸出“+1/{”,並且記錄輸出的’{‘的數量
  2. 如果p、q更新后不可進行操作,即\(p \% q == 0\),則說明到最后一個數了,不用輸出大括號,並且下一輪會結束循環
  3. 最后輸出等量的’}‘。

AC代碼:

#include <iostream>
#include <cstdio>

using namespace std;

int t, p, q;

int main()
{
    cin >> t;
    
    while(t--)
    {
        scanf("%d%d", &p, &q);
        printf("%d/%d = ", p, q);
        int kh = 0;
        while(q)
        {
            printf("%d", p / q);
            int t = p % q;
            p = q, q = t;
            if(q)
            {
                if(p % q != 0)
                {
                    printf("+1/{");
                    kh++;
                }
                else printf("+1/");
            }
        }
        while(kh--) printf("}");
        puts("");
    }
    return 0;
}

C.挪酒瓶

題目鏈接:https://ac.nowcoder.com/acm/contest/11210/C

參考題解:https://www.nowcoder.com/discuss/643079https://blog.nowcoder.net/n/efd1e10b345a42e1ae0231fc4ae9e7b4

對於一個有 n 個元素的置換群p,從最后一個 a往前開始換,則費用為 \((w_a+w_b)+(w_a+w_c)+...=(n−2)w_a+\sum_{i\in p}w_i\)

最優的策略則是選擇較小的 \(w_a\) 開始換。

對於多個置換群而言,單獨內部交換並不是最優解,可以把目前最輕的酒瓶拿進來替換,最后再換回去。令置換群 p 內最輕重量為 \(m_p=min_{i\in p}wi\),令全部的酒里面最輕重量為 \(m_{all}\)。這個策略的花費為 \(2(m_{all}+m_p)+(n−2)m_{all}+\sum_{i\in p}w_i - m_p + m_{all}\)(交換兩次最小值花費:\(2(m_{all}+m_p)\),交換后的所有費用和為:\(\sum_{i\in p}w_i - m_p + m_{all}\))

最后對於每一個置換群,考慮兩個 Case 如下,都選擇最優即可:

  • \((n−2)w_p+\sum_{i\in p}w_i\)
  • \(2(m_{all}+m_p)+(n−2)m_{all}+∑_{iεp}w_i - m_p + m_{all}\) = \((n + 1)*m_{all} + m_p + \sum_{i\in p}w_i\)

AC代碼:

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e5+10;

int a[N], w[N];
bool st[N];

int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        memset(st, 0, sizeof st);
        int n, ans = 0, min_all = 1e9;
        cin >> n;
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &w[i]);
            //找出全局最小值
            min_all = min(min_all, w[i]);
        }
        
        for(int i = 1; i <= n; i++)
            if(!st[i])
            {
                int cnt = 0, minv = 1e9;
                //求一個置換群
                for(int j = i; !st[j]; j = a[j])
                {
                    cnt++;
                    ans += w[j];
                    minv = min(minv, w[j]);
                    st[j] = true;
                }
                //最優解更新
                ans += min((cnt - 2) * minv, (cnt + 1) * min_all + minv);
            }
        cout << ans << endl;
    }
    
    return 0;
}

D.購物

題目鏈接:https://ac.nowcoder.com/acm/contest/11210/D

將物品名稱映射到數字,用數組記錄每一個物品的數量,然后遍歷每個人,將其沒有的物品的數量-1,最后遍歷計算物品數量大於0的個數。

AC代碼:

#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <cstring>

using namespace std;

const int N = 110;

int t, s, n;
int a[N];

int main()
{
    cin >> t;
    
    while(t--)
    {
        memset(a, 0, sizeof a);
        unordered_map<string, int> goods;
        string str;
        int x, idx = 0;
        scanf("%d%d", &s, &n);
        for(int i = 0; i < s; i++)
        {
            cin >> str >> x;
            goods.insert({str, idx++});
            a[goods[str]] = x;
        }
        //for(int i = 0; i < idx; i++) cout << a[i] << ' ';
        //puts("");
        for(int i = 0; i < n; i++)
        {
            bool has[N];
            memset(has, false, sizeof has);
            cin >> x;
            for(int j = 0; j < x; j++)
            {
                cin >> str;
                has[goods[str]] = true;
            }
            for(int i = 0; i < idx; i++)
                if(!has[i]) a[i]--;
        }
        int ans = 0;
        for(int i = 0; i < idx; i++)
            if(a[i] > 0) ans++;
        if(ans) cout << ans << endl;
        else puts("Need to be lucky");
    }
    return 0;
}

E.喝可樂

題目鏈接:https://ac.nowcoder.com/acm/contest/11210/E

直接枚舉每種可樂買多少瓶的所有情況,對每種情況計算能喝到的瓶數,記錄最大值即可。

AC代碼:

#include <iostream>
#include <algorithm>

using namespace std;

int t, n, a, b;

int main()
{
    cin >> t;
    while(t--)
    {
        int ans = 0;
        cin >> n >> a >> b;
        for(int i = 0; i <= n; i++)
        {
            int Max = n, an = i, bn = n - i;
            while(an >= a || bn >= b)
            {
                if(an >= a)
                {
                    Max += an / a;
                    bn += an / a;
                    an = an % a;
                }
                if(bn >= b)
                {
                    Max += bn / b;
                    an += bn / b;
                    bn = bn % b;
                }
            }
            ans = max(Max, ans);
        }
        cout << ans << endl;
    }
    return 0;
}

F.天旋地轉

題目鏈接:https://ac.nowcoder.com/acm/contest/11210/F

模擬,把坐標系的四種不同情況下的移動偏移量存下來,然后按步驟一步一步操作即可。

注意在計算逆時針旋轉時,標記坐標軸方向的數字fx需要變為非負數

AC代碼:

#include <iostream>
#include <algorithm>
#include <unordered_map>

#define x first
#define y second;

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

int t, n;
PII Move[4][4] = {{{0, 1}, {-1, 0}, {1, 0}, {0, -1}},
{{-1, 0}, {0, -1}, {0, 1}, {1, 0}},
{{0, -1}, {1, 0}, {-1, 0}, {0, 1}},
{{1, 0}, {0, 1}, {0, -1}, {-1, 0}}};
unordered_map<char, int> f {{'w', 0}, {'a', 1}, {'d', 2}, {'s', 3}};

int main()
{
    cin >> t;
    while(t--)
    {
        LL xx = 0, yy = 0, fx = 0, k;
        cin >> n;
        while(n--)
        {
            char c;
            cin >> c >> k;
            if(c == 'r') fx = (fx + k) % 4;
            else if(c == 'l') fx = ((fx - k) % 4 + 4) % 4;
            else xx += k * Move[fx][f[c]].x, yy += k * Move[fx][f[c]].y;
            //cout << fx << ' ' << xx << ',' << yy << endl;
        }
        cout << xx << ' ' << yy << endl;
    }
    return 0;
}

G.切圈圈

題目鏈接:https://ac.nowcoder.com/acm/contest/11210/G

首先預處理出前綴和數組。可以知道性質:前綴和數組中相等的兩點,之間的區間和一定是0。題目已經給出整個數組的和為0,那么只要確定數組中的一段和為0的區間,則另外一段(包含首尾的那一段)也就一定區間和為0了。所以可以直接在一維線性結構上進行求解。

當已經確定了一個區間和為0的區間時,這區間的端點前綴和數組的值一定相等。如果該區間內可以再分,由性質可得,區間內的切分點的前綴和數組值一定和兩個端點的值相等。(環的另外一段也是如此)

由此可以進一步推出:最后分割出來的區間,所有端點的前綴和數組值一定相等。

所以,只要求出前綴和數組值相等的最大數量,即為答案。

AC代碼:

#include <iostream>
#include <algorithm>
#include <map>

using namespace std;

const int N = 10010;

int t, n;
int a[N];

int main()
{
    cin >> t;
    while(t--)
    {
        cin >> n;
        int ans = 0;
        map<int, int> m;
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            //預處理前綴和數組
            a[i] += a[i - 1];
            //記錄值為a[i]的點有多少個
            m[a[i]]++;
            ans = max(ans, m[a[i]]);
        }
        cout << ans << endl;
    }
    return 0;
}

H.貨物運輸

題目鏈接:https://ac.nowcoder.com/acm/contest/11210/H

根據題目建有向圖,然后直接套最短路模板即可。

關於建圖,難點在處理好邊權:

邊權:w[i] = c * min(f, d) + max(0, f - d) * cc

AC代碼:

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long LL;

const int N = 110;

int t, n, m, s, ee, f;
int a, b, c, d, cc;
int h[N], e[N * N], ne[N * N], idx;
LL w[N * N], dist[N];
bool st[N];

void add(int a, int b, LL c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

void dij(int u)
{
    dist[u] = 0;
    
    for(int i = 0; i < n; i++)
    {
        int k = -1;
        for(int j = 1; j <= n; j++)
            if(!st[j] && (k == -1 || dist[k] > dist[j]))
                k = j;
        
        for(int j = h[k]; j != -1; j = ne[j])
        {
            dist[e[j]] = min(dist[e[j]], dist[k] + w[j]);
        }
        
        st[k] = true;
    }
}

int main()
{
    cin >> t;
    while(t--)
    {
        idx = 0;
        memset(h, -1, sizeof h);
        memset(dist, 0x3f, sizeof dist);
        memset(st, false, sizeof st);
        scanf("%d%d%d%d%d", &n, &m, &s, &ee, &f);
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d%d%d%d", &a, &b, &c, &d, &cc);
            //注意d、f的大小
            add(a, b, (LL)c * min(d, f) + (LL)max(0, f - d) * cc);
        }
        dij(s);
        //for(int i = 1; i <= n; i++)
        //    cout << i << ':' << dist[i] << endl;
        printf("%lld\n", dist[ee]);
    }
    
    return 0;
}

I.三角尼姆

題目鏈接:https://ac.nowcoder.com/acm/contest/11210/I

首先手動模擬發現:N = 1時Alice必贏,此時一共有1個空位;N = 2時Alice必贏,此時一共有3個空位;N = 3時Bob必贏,此時一共有6個空位;N = 4時Bob必贏,此時一共有10個空位。。。。。。

大膽猜測出:當空位總數為偶數個是,先手必贏,否則先手必輸

經代碼檢驗猜測正確。

證明:最后輸的條件一定是只剩1個空位,往前推一下就是倒數第二次放棋子一定是3個空位,即最后面對的必輸局面是剩下奇數個空位

由:奇數 - 奇數 = 偶數,偶數 - 奇數 = 奇數,且每次減少空位一定是1或者3(奇數)

可得:剩余空位數一定是以“奇偶奇偶...”或者“偶奇偶奇...”的順序出現的

進一步可得:如果玩家第一次面對的是偶數個空位,則一直是面對偶數個空位,則一定不會碰到奇數的情況,就一定不會輸

AC代碼:

#include <iostream>
#include <algorithm>

using namespace std;

int t, n;

int main()
{
    cin >> t;
    while(t--)
    {
        cin >> n;
        n = (1 + n) * n / 2;
        if(n & 1) puts("Alice");
        else puts("Bob");
    }
    return 0;
}

J.線段的交

題目鏈接:https://ac.nowcoder.com/acm/contest/11210/J

分兩步:判斷線段是否相交、求面積。

解法一:利用向量叉積判斷線段相交及求四邊形面積。

線段相交

AC代碼:

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

double x1, yy1, x2, y2, x3, y3, x4, y4;

struct Point
{
    double x, y;
}p1, p2, q1, q2;

double cj(Point p, Point q)
{
    return p.x * q.y - q.x * p.y;
}

double area(Point p1, Point p2, Point q1)
{
    Point xl1 = {p2.x - p1.x, p2.y - p1.y};
    Point xl2 = {q1.x - p1.x, q1.y - p1.y};
    return cj(xl1, xl2);
}

int main()
{
    scanf("%lf%lf%lf%lf%lf%lf%lf%lf", &x1, &yy1, &x2, &y2, &x3, &y3, &x4, &y4);
    p1 = {x1, yy1}, p2 = {x2, y2}, q1 = {x3, y3}, q2 = {x4, y4};
    
    double s1 = area(p1, p2, q1);
    double s2 = area(p1, p2, q2);
    double s3 = area(q1, q2, p1);
    double s4 = area(q1, q2, p2);
    if(s1 * s2 <= 0 && s3 * s4 <= 0) printf("%.8f\n", (fabs(s1) + fabs(s2)) / 2);
    else cout << 0 << endl;
    
    return 0;
}

解法二:利用快速排斥實驗和跨立實驗判斷直線相交,在用向量叉積求四邊形面積

WA代碼(case通過率92%,bug未知):

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

double x1, yy1, x2, y2, x3, y3, x4, y4;

struct Point
{
    double x, y;
}p1, p2, q1, q2;
//快速排斥實驗
bool kspc(Point p1, Point p2, Point q1, Point q2)
{
    double minRx = min(p1.x, p2.x), minRy = min(p1.y, p1.y);
    double maxRx = max(p1.x, p2.x), maxRy = max(p1.y, p2.y);
    double minTx = min(q1.x, q2.x), minTy = min(q1.y, q2.y);
    double maxTx = max(q1.x, q2.x), maxTy = max(q1.y, q2.y);
    double minFx = max(minRx, minTx), minFy = max(minRy, minTy);
    double maxFx = min(maxRx, maxTx), maxFy = min(maxRy, maxTy);
    return (minFx <= maxFx && minFy <= maxFy);
}
//求向量叉積
double cj(Point p, Point q)
{
    return p.x * q.y - q.x * p.y;
}
//跨立實驗
bool kl(Point p1, Point p2, Point q1, Point q2)
{
    Point xl1 = {p1.x - q1.x, p1.y - q1.y}, xl2 = {q2.x - q1.x, q2.y - q1.y}, xl3 = {p2.x - q1.x, p2.y - q1.y};
    Point xl4 = {q1.x - p1.x, q1.y - p1.y}, xl5 = {p2.x - p1.x, p2.y - p1.y}, xl6 = {q2.x - p1.x, q2.y - p1.y};
    return (cj(xl1, xl2) * cj(xl2, xl3) >= 0 && cj(xl4, xl5) * cj(xl5, xl6) >= 0);
}
//求面積
void area()
{
    /*
    double s1 = fabs(p1.x * p2.y - p2.x * p1.y + p2.x * q1.y - q1.x * p2.y + q1.x * p1.y - p1.x * q1.y) / 2;
    double s2 = fabs(p1.x * p2.y - p2.x * p1.y + p2.x * q2.y - q2.x * p2.y + q2.x * p1.y - p1.x * q2.y) / 2;
    */
    Point xl1 = {p2.x - p1.x, p2.y - p1.y};
    Point xl2 = {q1.x - p1.x, q1.y - p1.y};
    Point xl3 = {q2.x - p1.x, q2.y - p1.y};
    printf("%.8lf\n", (fabs(cj(xl1, xl2)) + fabs(cj(xl1, xl3))) / 2);
}

int main()
{
    scanf("%lf%lf%lf%lf%lf%lf%lf%lf", &x1, &yy1, &x2, &y2, &x3, &y3, &x4, &y4);
    p1 = {x1, yy1}, p2 = {x2, y2}, q1 = {x3, y3}, q2 = {x4, y4};
    
    if(kspc(p1, p2, q1, q2) && kl(p1, p2, q1, q2)) area();
    else cout << 0 << endl;
    
    return 0;
}


免責聲明!

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



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