湖南大学第十六届程序设计竞赛(重现赛)补题报告
湖南大学第十六届程序设计竞赛(重现赛)补题报告
比赛链接: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\),可以进行的两种操作如下:
- 将 \(k\) 变为 \(10*k\)
- 将 \(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
标签:字符串、思维
题目大意
给两个字符串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
标签:思维,假设法,二分
题目大意
现有一个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
标签:搜索
题目大意
给了一张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;
}

浙公网安备 33010602011771号