湖南大学第十六届程序设计竞赛(重现赛)补题报告

湖南大学第十六届程序设计竞赛(重现赛)补题报告

比赛链接: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;
}
posted @ 2021-08-05 13:55  nodot08  阅读(37)  评论(0)    收藏  举报