1. 程式人生 > 其它 >湖南大學第十六屆程式設計競賽(重現賽)補題報告

湖南大學第十六屆程式設計競賽(重現賽)補題報告

內容:A(平面幾何)、B(簡單搜尋)、D(01分佈期望)、F(高精度)、G(思維)、I(假設法+二分)、L(簡單搜尋)

湖南大學第十六屆程式設計競賽(重現賽)補題報告

比賽連結:https://ac.nowcoder.com/acm/contest/18196#question

A

連結:Triangles

標籤:三角形、平面幾何

題目大意

給平面中三個點的座標,判斷以這三個點能否構成三角形?

若能構成,判斷構成的是直角、銳角還是鈍角三角形?

解題思路

判斷能否構成三角形的方法可以用兩向量平行、或半周長大於任意邊長

判斷構成三角形的型別可用餘弦定理,求出三邊長的平方,用較小的兩個減去最大的一個,即可根據結果判斷是直角(=0),銳角(<0),鈍角(>0)

程式碼

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

using namespace std;

int x[4], y[4], d[4];

void solve()
{
    for (int i = 1; i <= 3; i ++) cin >> x[i] >> y[i];
    d[1] = (x[1] - x[2]) * (x[1] - x[2]) + (y[1] - y[2]) * (y[1] - y[2]);
    d[2] = (x[1] - x[3]) * (x[1] - x[3]) + (y[1] - y[3]) * (y[1] - y[3]);
    d[3] = (x[2] - x[3]) * (x[2] - x[3]) + (y[2] - y[3]) * (y[2] - y[3]);
    sort(d + 1, d + 4);

    // 兩向量平行:a / b == c / d --> a * d == c * b;
    if ((x[1] - x[2]) * (y[3] - y[2]) == (y[1] - y[2]) * (x[3] - x[2]))
        cout << "invalid" << endl;
    else if (d[1] + d[2] == d[3]) cout << "right" << endl;
    else if (d[1] + d[2] < d[3]) cout << "obtuse" << endl;
    else cout << "acute" << endl;
}


int main()
{
    int _;
    cin >> _;
    while (_ --) solve();
    return 0;
}

B. Yuki with emofunc and playf

連結:Yuki with emofunc and playf

標籤:BFS

題目大意

給2個正整數 \(N,x\)​​​,你需要通過進行多次下列兩種操作將1變為N的倍數,求最少的操作次數.

假設當前的數為 \(k\)​,可以進行的兩種操作如下:​​

  1. \(k\) 變為 \(10*k\)
  2. \(k\)​ 變為 \(k+x-1\)

解題思路

要求最終變為N的倍數,搜尋考慮的範圍為0~N-1,寬搜即可.

程式碼

#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 1e6 + 10;

bool vis[N];
queue<pair<int, int>> q;

int main()
{
    int x, y, n, ok = 0;
    cin >> n >> x;
    x = (x - 1 + n) % n;
    y = 10 % n;

    q.push({1, 0});
    while (!q.empty())
    {
        auto u = q.front(); q.pop();
        vis[u.first] = true;
        if (u.first % n == 0) {
            cout << u.second << endl;
            ok = 1;
            break;
        }
        if (vis[(u.first + x) % n] == 0)
            q.push({(u.first + x) % n, u.second + 1});
        if (vis[(u.first * y) % n] == 0)
            q.push({(u.first * y) % n, u.second + 1});
    }
    if (ok == 0) cout << -1 << endl;

    return 0;
}

D. Queuing

連結:Queuing

標籤:\(01\) 分佈、數學期望

題目大意

有n個視窗,每個人去到其中任一視窗排隊的概率都是1/n,

有m個人依次去排隊,問第m個人排隊時,其在隊伍中的位次的數學期望.

解題思路

先想下前 \(m-1\) 個人的分佈情況

假設第 \(m\) 個人選擇視窗 \(k\),對於每個人,只考慮選視窗 \(k\) 或選其他視窗兩種情況

顯然,前 \(m-1\)​ 個人服從 \(01\) 分佈

\(01\) 分佈的數學期望公式 \(E = np\) ,得 \(E = (m-1) * 1/n\).

即第 \(m\) 個人前面人數的數學期望

所以,第 \(m\) 個人位次的數學期望為 \((m-1) * 1/n + 1\)

​.

F. Team

連結:Team

標籤:高精度

題目大意

給你三個正整數 \(a, b, c\) 求這三個數之和.

資料範圍:\(2^{62}≤a,b≤2^{63},0≤c≤2\)

解題思路

高精度加法

程式碼

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

const int N = 1e5 + 50;

void add(int *a, int *b, int &n, int m) {
    int len = max(n, m), q = 0;
    for (int i = 0; i < len; i ++)
    {
        a[i] += b[i] + q;
        q = a[i] / 10;
        a[i] %= 10;
    }
    if (q)
    {
        a[len] = q;
        n = len + 1;
    }
    else n = len;
}

void print(int *a, int n)
{
    for (int i = n - 1; i >= 0; i --) cout << a[i];
    cout << endl;
}

int x[N], y[N], z[N];

int main()
{
    string a, b, c;
    cin >> a >> b >> c;
    int n = a.size();
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    reverse(c.begin(), c.end());
    for (int i = 0; i < a.size(); i ++) x[i] = a[i] - '0';
    for (int i = 0; i < b.size(); i ++) y[i] = b[i] - '0';
    for (int i = 0; i < c.size(); i ++) z[i] = c[i] - '0';
    add(x, y, n, b.size());
    add(x, z, n, c.size());
    print(x, n);
    return 0;
}

G.Binbin's string

連結:Binbin's string

標籤:字串、思維

題目大意

給兩個字串S和T,需要通過插入(或刪除)任意字串將S變成T.

問需要插入和刪除操作的最少次數.

解題思路

通過對整個字串操作可知需要的最多次數為2

當S與T相同時,操作次數為0

當操作次數為1時,分析可知,較長的字串是較短的字串插入得來的

也就是說,較短的字串是較長的字串的前後綴拼接而成

對S和T從前面和後面各匹配一次即可判斷.

程式碼

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>

using namespace std;

int main()
{
    string s, t;
    cin >> s >> t;

    if (s.length() > t.length()) swap(s, t);
    int n = s.length(), m = t.length();
    int p1 = n, p2 = n;
    for (int i = 0; i < n; i ++) {
        if (s[i] != t[i]) {
            p1 = i;
            break;
        }
    }
    for (int i = 0; i < n; i++) {
        if (s[n - i - 1] != t[m - i - 1]) {
            p2 = i;
            break;
        }
    }
    if (s == t) cout << 0 << endl;
    else if (p1 + p2 == n) cout << 1 << endl;
    else cout << 2 << endl;

    return 0;
}

I.Binbin and Balls

連結:Binbin and Balls

標籤:思維,假設法,二分

題目大意

現有一個n層的大樓,你只有兩個球,求:

在最壞情況下,測出球落下後不會爆炸的最高樓層數所需要的最少嘗試次數.

注意:如果在球某層落下後爆炸,則球在更高的樓層落下也會爆炸.

解題思路

聯想二分的思想。

為了使最壞情況下嘗試次數儘可能少,那麼在用第一個球嘗試時出現的兩種情況需要的後續次數應該相同.

假設第一次試的是T層,如果爆炸,那麼需要從第一層往上試,最壞情況下總共的次數為T

如果沒爆炸,那麼再去試T+T-1層,如果爆炸,最壞情況下需要的次數總共也為T次

如果還是沒爆炸,再去試T+T-1+T-2層,後面同理.

如果一直沒爆炸,那麼最後試的總次數也為T次

顯然,在這種方法中T的選取與最高樓層是相關的,要求 \(\Sigma_{i=1}^T i >= n\).

因此可以通過二分來求出最小滿足條件的T.

程式碼

#include <iostream>

using namespace std;

long long sum(long long n) {
	return (1 + n) * n / 2;
}

int main()
{
	int _;
	cin >> _;
	while (_ --) {
		long long n, l = 0, r = 1e10;
		cin >> n;
		while (l < r) {
			long long mid = (l + r) / 2;
			if (sum(mid) >= n) r = mid;
			else l = mid + 1;
		}
		cout << r << endl;
	}
	return 0;
}

L. Cracked Pipes

連結:Cracked Pipes

標籤:搜尋

題目大意

給了一張n*m的圖,問(1,0)能否與(n,m+1)聯通.

具體見題目原文

解題思路

BFS或DFS直接搜

程式碼

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>

using namespace std;

const int N = 2e5 + 10;
vector<int> vis[N], dir[N];

#define LF 0
#define RT 1
#define UP 2
#define DN 3

bool hv(int t, int d)
{
    if (t == 1) return d == UP || d == DN;
    if (t == 2) return d == LF || d == RT;
    if (t == 3) return d == DN || d == RT;
    if (t == 4) return d == LF || d == UP;
    if (t == 5) return d == LF || d == RT || d == UP;
    if (t == 6) return d == LF || d == RT || d == DN;
}

void solve()
{
    int n, m;
    cin >> n >> m;

    for (int i = 1; i <= n; i ++)
    {
        vis[i].resize(m + 1);
        dir[i].resize(m + 1);
        for (int j = 1; j <= m; j ++) cin >> dir[i][j];
    }

    queue<pair<int, int>> q;
    if (hv(dir[1][1], LF)) q.push({1, 1}), vis[1][1] = 1;
    while (!q.empty())
    {
        int x = q.front().first, y = q.front().second; q.pop();
        if (x + 1 <= n && hv(dir[x][y], DN) && hv(dir[x + 1][y], UP) && !vis[x + 1][y])
            q.push({x + 1, y}), vis[x + 1][y] = 1;
        if (x - 1 >= 1 && hv(dir[x][y], UP) && hv(dir[x - 1][y], DN) && !vis[x - 1][y])
            q.push({x - 1, y}), vis[x - 1][y] = 1;
        if (y + 1 <= m && hv(dir[x][y], RT) && hv(dir[x][y + 1], LF) && !vis[x][y + 1])
            q.push({x, y + 1}), vis[x][y + 1] = 1;
        if (y - 1 >= 1 && hv(dir[x][y], LF) && hv(dir[x][y - 1], RT) && !vis[x][y - 1])
            q.push({x, y - 1}), vis[x][y - 1] = 1;
    }
    if (vis[n][m] && hv(dir[n][m], RT)) cout << "YES" << endl;
    else cout << "NO" << endl;
}


int main()
{
    int _ = 1;
//    cin >> _;
    while (_ --) solve();
    return 0;
}