湖南大學第十六屆程式設計競賽(重現賽)補題報告
湖南大學第十六屆程式設計競賽(重現賽)補題報告
比賽連結: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;
}