2021年合肥学院程序设计竞赛
2021年合肥学院校赛题解
A.我爱合院
思路
直接输出"HFUU"
即可。
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
cout << "HFUU";
return 0;
}
B.简单的工资发放问题
思路
第一天的工资为1
,后面两天的工资为2, 2
,后面三天的工资为3,3,3
......
数据范围为1 - 10000
,方法很多, 我们可以先预处理出每天的工资,然后循环一次累加每天的工资即可。时间复杂度O(n)
。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10010;
LL a[N];
int n;
int main()
{
int n;
scanf("%d", &n);
int qwq = 0;
int flag = 1;
for(int i = 1; i <= n; i ++ )
{
a[i] = flag;
++ qwq;
if(qwq == flag)
{
flag ++;
qwq = 0;
}
}
LL ans = 0;
for(int i = 1; i <= n; i ++ ) ans += a[i];
printf("%lld",ans);
return 0;
}
C.简单的计算几何问题
思路
本题我们可以对所有的点求一个凸包,答案就是凸包的周长。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define x first
#define y second
using namespace std;
const int N = 10010;
typedef pair<double,double> PDD;
int n;
PDD q[N];
int stk[N];
bool st[N];
PDD operator-(PDD a, PDD b)
{
return {a.x - b.x, a.y - b.y};
}
double cross(PDD a, PDD b)
{
return a.x * b.y - b.x * a.y;
}
double area(PDD a, PDD b, PDD c)
{
return cross(b - a, c - a);
}
double get_dist(PDD a, PDD b)
{
double xx = a.x - b.x;
double yy = a.y - b.y;
return sqrt(xx * xx + yy * yy);
}
double Andrew()
{
sort(q + 1, q + 1 + n);
int top = 0;
for(int i = 1; i <= n; i ++ )
{
while(top >= 2 && area(q[stk[top - 1]], q[stk[top]], q[i]) <= 0)
{
if(area(q[stk[top - 1]], q[stk[top]], q[i]) < 0)
st[stk[top -- ]] = false;
else top --;
}
stk[ ++ top] = i;
st[i] = true;
}
st[1] = false;
for(int i = n; i >= 1; i -- )
{
if(st[i]) continue;
while(top >= 2 && area(q[stk[top - 1]], q[stk[top]], q[i]) <= 0)
{
if(area(q[stk[top - 1]], q[stk[top]], q[i]) < 0)
st[stk[top -- ]] = false;
else top --;
}
stk[++ top] = i;
st[i] = true;
}
double res = 0;
for(int i = 2; i <= top; i ++ )
res += get_dist(q[stk[i]], q[stk[i - 1]]);
return res;
}
int main()
{
scanf("%d",&n);
for(int i = 1; i <= n; i ++ ) scanf("%lf %lf",&q[i].x, &q[i].y);
double res = Andrew();
printf("%.2lf", res);
return 0;
}
D.简单的理财问题
思路:
本题是纯模拟题。
整个过程是确定的,从前往后依次模拟整个流程即可。
模拟过程中记录如下几个值:
total
: 存在姐姐那里的总钱数;
remain
: bs
当前剩在自己手里的钱数;
cur
:读入的值,表示当前这个月的生活开销
依次枚举每个月,对于当前这个月,首先判断总共能拿到的钱数是否够用,即判断:
cur + 300 >= cur
是否成立;
如果够用,则将整百部分交给姐姐,自己剩下最多两位数的钱。
最后的总钱数是:total * 12 + remain
。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int total = 0, remain = 0;
for (int i = 1; i <= 12; i ++ )
{
int cur;
cin >> cur;
if (remain + 300 < cur)
{
total = -i;
break;
}
remain += 300 - cur;
total += remain / 100 * 120;
remain %= 100;
}
if (total >= 0) total += remain;
cout << total << endl;
return 0;
}
E.简单的路径规划问题
思路
裸的最短路问题,方法很多,套朴素Dijkstra
都能过,堆优化的当然更好。
朴素Dijkstra
的时间复杂度 \(O(n^2)\)。
优化的Dijkstra
的时间复杂度为\(O(nlogn)\)。
代码
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 510;
int n, m;
int g[N][N];
int dist[N];
bool st[N];
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 0; i < n - 1; i ++ )
{
int t = -1;
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
for (int j = 1; j <= n; j ++ )
dist[j] = min(dist[j], dist[t] + g[t][j]);
st[t] = true;
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main()
{
scanf("%d%d", &n, &m);
memset(g, 0x3f, sizeof g);
while (m -- )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
g[a][b] = min(g[a][b], c);
}
printf("%d\n", dijkstra());
return 0;
}
F.简单的字符串问题
思路:
本题要求长度大于\(1\)且最短的回文串,只有三种情况:
\(1\).字符串里面出现连续两个相同的字符 , \(exp: .....aa.....\) 答案是$ 2 $
\(2\).字符串中出现长度为3的回文串,例如: $...qwq... $答案是 \(3\)
\(3\).字符串中只有长度为1的回文串例如:\(abcdefg\),答案是 \(-1\)
其余的回文串如果存在则一定长于第一第二种情况,所以答案只有\(2, 3, -1\)。
时间复杂度为\(O(n)\)。\(n\)为字符串的长度。
代码:
#include <cstring>
#include <iostream>
using namespace std;
string s;
int main()
{
cin >> s;
int len = s.length();
for(int i = 1; i < len; i ++ )
{
if(s[i] == s[i - 1]) {
printf("2\n");
return 0;
}
}
for(int i = 1; i < len; i ++ )
{
if(s[i - 1] == s[i + 1] && i + 1 < len){
printf("3\n");
return 0;
}
}
printf("-1\n");
return 0;
}
G.简单的排序问题
思路:
本题的思路很明显,先把所有的时间段排个序,然后选择时间段较长的即可,排序的话大部分排序方式都能过,冒泡也能过。
时间复杂度是关于你的排序方式,冒泡为\(O(n^2)\)或者快排是\(O(nlogn)\)
代码:
#include <stdio.h>
#define N 10010
int main()
{
int n;
int a[N];
scanf("%d",&n);
for(int i = 1; i<= n; i ++ ) scanf("%d",&a[i]);
int temp, i, j;
for (i = 1; i <= n; i++) //进行9次比较
for (j = 1; j <= n - i; j++) //在每次中进行10-i-1次比较
if (a[j] > a[j + 1]) //按升序排序,降序用<
{
temp = a[j + 1]; //交换相邻的两个元素
a[j + 1] = a[j];
a[j] = temp;
}
int sum = 0;
for (i = n / 2 + 1; i <= n; i++) sum += a[i];
printf("%lld", sum);
}
H.简单的桌游问题
思路:
题意可以理解成不能出现边长都为1的三角形路径,根据这个要求,可以发现,当将一个元素作为起点,遍历剩余的棋子作为一个个终点,这样的连接方式是符合题意要求的。同时,为了保证答案的最大化,我们可以将更多的棋子作为起点方,剩余的棋子当作终点方,显然当起点和终点的棋子差距最小时,答案是最大的,所以本题可以等价成左右两排数求最多的连接数。
时间复杂度为\(O(1)\).
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 998244353;
int main() {
int n;
while (cin >> n)
cout << (n / 2) * (n - n / 2) << endl;
return 0;
}
I.简单的数据结构问题
思路:
本题的每组数据的答案都在一个不断更新的长度为一整天的数组中,注意到每次输入时的ti是递增的,所以可以类比成一个滑动的窗口不断单调滑动,所以本题我们需要用到队列来处理滑动窗口,用队列中队头和队尾的更新来模拟窗口的滑动,在滑动时不断更新数组储存的国籍数据。
时间复杂度\(O(K)\)。
代码:
#include<iostream>
using namespace std;
int s,i,n,t,k,r,w[100001],x[300002],y[300002];
int main(){
cin>>n;
while(n--){
cin>>t>>k;
while(k--){
y[++r]=t;cin>>x[r];
if(!w[x[r]])s++;
w[x[r]]++;
}
while(t-y[i]>=86400)
if(!--w[x[i++]])s--;
cout<<s<<endl;
}
return 0;
}
J.合院生日快乐
思路:
输出$ n - 40 + 2020$即可
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
cout << n - 40 + 2020 << endl;
return 0;
}