2023 SZTU-ACM迎新赛
写在前面
难度 | 题号 | 考察点 |
---|---|---|
签到 | \(I\ M\) | 基本的语言知识 |
简单 | \(L\ B\ G\ E\) | 枚举、贪心等基础算法思想 |
中等 | \(D\ F\ K\ H\) | 基础算法和代码实现能力 |
困难 | \(J\ A\ C\) | 基础算法的进阶 |
预估:冠军 \(8\) 题,金牌 \(5\) 题,银牌 \(4\) 题
A 要迟到了!
考点:
- 搜索
- 图论
题解:
困难题
解法一:两遍 \(BFS\) \(O(n*m + k)\)
从起点开始做 \(BFS\) 记录从起点到每个位置只走路(每步时间为 \(t_1\) )的时间 \(t_{w}\)
从终点开始做 \(BFS\) 记录从终点到每个位置用单车(每步时间为 \(t_2\) )的时间 \(t_c\)
维护一个最小值 \(ans\)
遍历所有的单车的位置 \((i,j)\) ,此时 \(ans = min(ans,\ t_w[i][j] + t_c[i][j])\)
再额外判断一次如果不找单车,直接用走路的方式从起点到终点 \((i,j)\) ,即 \(ans = min(ans,\ t_w[i][j])\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
int n, m, t1, t2;
pair<int, int> S, T;
vector<pair<int, int>> C;
char mp[N][N];
vector<vector<int>> t_w(N, vector<int>(N, -1));
vector<vector<int>> t_c(N, vector<int>(N, -1));
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};
bool vis[N][N];
void bfs(pair<int, int> PII, vector<vector<int>> &t, int dt)
{
memset(vis, 0, sizeof(vis));
queue<pair<int, int>> q;
vis[PII.first][PII.second] = true;
t[PII.first][PII.second] = 0;
q.push(make_pair(PII.first, PII.second));
while (!q.empty())
{
int x = q.front().first;
int y = q.front().second;
q.pop();
for (int i = 0; i < 4; i++)
{
int tx = x + dx[i];
int ty = y + dy[i];
if (tx < 1 || ty < 1 || tx > n || ty > m || vis[tx][ty] || mp[tx][ty] == '#')
continue;
vis[tx][ty] = true;
t[tx][ty] = t[x][y] + dt;
q.push(make_pair(tx, ty));
}
}
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
cin >> n >> m >> t1 >> t2;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> mp[i][j];
if (mp[i][j] == 'S')
S = make_pair(i, j);
else if (mp[i][j] == 'T')
T = make_pair(i, j);
else if (mp[i][j] == 'C')
C.push_back(make_pair(i, j));
}
}
bfs(S, t_w, t1);
bfs(T, t_c, t2);
int ans = 0x3f3f3f3f;
for (auto it : C)
{
int cx = it.first;
int cy = it.second;
ans = min(ans, t_w[cx][cy] + t_c[cx][cy]);
}
ans = min(ans, t_w[T.first][T.second]);
cout << ans;
return 0;
}
解法二:BFS+优先队列 \(O(2nm \log (nm))\)
使用优先队列让最短时间在队首,时间相同的时候优先有车的。
\(BFS\) 直到到达终点,此时的时间即为答案。若队列结束也没有到达终点则返回 \(-1\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
int n, m, t1, t2;
int sx, sy;
char mp[N][N];
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};
bool vis[N][N][2];
struct node
{
int x, y, t;
bool flag;
friend bool operator<(const node a, const node b)
{
if (a.t != b.t)
return a.t > b.t;
return a.flag < b.flag;
}
};
int bfs()
{
priority_queue<node> q;
q.push({sx, sy, 0, 0});
vis[sx][sy][0] = true;
while (!q.empty())
{
node now = q.top();
int x = now.x;
int y = now.y;
q.pop();
if (mp[x][y] == 'T')
return now.t;
for (int i = 0; i < 4; i++)
{
int tx = x + dx[i];
int ty = y + dy[i];
int tt = now.t + (now.flag ? t2 : t1);
bool flag = now.flag || mp[tx][ty] == 'C';
if (tx < 1 || ty < 1 || tx > n || ty > m || vis[tx][ty][flag] || mp[tx][ty] == '#')
continue;
vis[tx][ty][flag] = true;
q.push({tx, ty, tt, flag});
}
}
return -1;
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
cin >> n >> m >> t1 >> t2;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> mp[i][j];
if (mp[i][j] == 'S')
sx = i, sy = j;
}
}
cout << bfs();
return 0;
}
B 打怪兽
考点:
- 遍历
题解:
简单题
遍历所有的怪兽,求出每个怪兽对任何一个小蔡的最短距离,输出其中的最大值即可。
复杂度: \(O(n*k)\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <bits/stdc++.h>
using namespace std;
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int n, k;
cin >> n >> k;
vector<int> v(k + 10);
for (int i = 1; i <= k; i++)
cin >> v[i];
vector<pair<int, int>> a(n + 10);
for (int i = 1; i <= n; i++)
cin >> a[i].first >> a[i].second;
double ans = 0;
for (int i = 1; i <= n; i++)
{
int x = a[i].first;
int y = a[i].second;
double t = 1e18;
for (int j = 1; j <= k; j++)
{
int xx = a[v[j]].first;
int yy = a[v[j]].second;
t = min(t, sqrt(pow(x - xx, 2) + pow(y - yy, 2)));
}
ans = max(ans, t);
}
cout << fixed << setprecision(10) << ans;
return 0;
}
C 演唱会
考点:
- 动态规划
题解:
困难题
定义了一个结构体 node
,其中包含了每一天的信息,包括慧慧可以完成的任务数量 \(l\) ,完成一项任务的薪资 \(q\) ,以及这一天是哪一天 \(s\) 。此外,我们还计算了这一天可以完成任务的时间段 \(ledge\) 和 \(redge\) 。
定义了一个二维数组 dp
,其中 dp[i][j]
表示在第 \(i\) 天结束时,如果慧慧选择最后提前做到第 \(j\) 天工作,她可以获得的最大收入。
对于每一天,我们有两种选择:要么选择工作,要么选择休息。如果选择工作,我们需要更新 dp[i][j]
的值。为了找到最优的工作日期,我们使用了一个优先队列 que
来存储可能的工作日期。在每一天结束时,我们检查队列中的任务是否可以在当前日期完成,如果可以,我们就更新 dp[i][j]
的值。
优先队列 que
的作用是帮助我们在 \(O(1)\) 的时间复杂度内找到最优的工作日期。具体来说,优先队列中存储的是一对值,第一个值是 \(dp[i - 1][j] - j * day[i].q\) ,表示如果在第 \(j\) 天工作,那么慧慧可以获得的收入;第二个值是 \(j\) ,表示这一天是哪一天。
在每一天结束时,我们检查队列中的任务是否可以在当前日期完成,如果可以,我们就更新 dp[i][j]
的值。为了实现这一点,我们在每一天开始时,都会将可能的工作日期添加到优先队列中。然后,在每一天结束时,我们检查队列的顶部元素,如果这一天的任务可以在当前日期完成,那么我们就从队列中弹出这个元素,并更新 dp[i][j]
的值。
最后,我们遍历 dp[k]
数组,找到最大的收入,这就是慧慧可以获得的最大收入。
复杂度: \(O(n*k)\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <bits/stdc++.h>
using namespace std;
const int N = 16000 + 10;
struct node
{
int s, l, q;
int ledge, redge;
friend bool operator<(const node &a, const node &b)
{
return a.s < b.s;
}
} day[105];
int n, k;
int dp[105][N];
int main()
{
cin >> n >> k;
for (int i = 1; i <= k; i++)
{
cin >> day[i].l >> day[i].q >> day[i].s;
day[i].ledge = max(0, day[i].s - day[i].l);
day[i].redge = min(n, day[i].s + day[i].l - 1);
}
sort(day + 1, day + 1 + k);
for (int i = 1; i <= k; i++)
{
for (int j = 1; j < day[i].s; j++)
dp[i][j] = dp[i - 1][j];
priority_queue<pair<int, int>> que;
for (int j = day[i].ledge; j < day[i].s; j++)
{
int cur = dp[i - 1][j] - j * day[i].q;
while (!que.empty() && dp[i - 1][que.top().second] - que.top().second * day[i].q <= cur)
que.pop();
que.push({cur, j});
}
for (int j = day[i].s; j <= day[i].redge; j++)
{
while (!que.empty() && j - que.top().second > day[i].l)
que.pop();
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
dp[i][j] = max(dp[i][j], dp[i - 1][que.top().second] + (j - que.top().second) * day[i].q);
}
for (int j = day[i].redge + 1; j <= n; j++)
{
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
int ans = 0;
for (int i = 1; i <= n; i++)
ans = max(ans, dp[k][i]);
cout << ans << endl;
return 0;
}
D 总要赢一次吧
考点:
- \(bash\) 博弈
题解:
中等题
由题可知,本题需要找先手必胜的条件。
对于总量为 \(v\) 升的酒,如果每人每次只能喝 \(1 \sim k\) 升的酒。想赢就需要让对方喝完后还有剩余,并且在 \(1 \sim k\) 升之间。所以只要让酒一直是 \(n(k+1)\ (n \in N)\) 升就一定能够赢。
证明:当酒为 \(n(k+1)\ (n \in N)\) 时,如果对方喝 \(a\) 升,我们一定能找到 \(b\) 满足 \(a+b=k+1\) ,重复此过程,直到还有 \(k+1\) 升酒。此时不论对方喝多少,我们都可以喝最后一次。
故我们只需要找到满足 \(v\%(k+1)\ne 0\) 的情况,此时先手必胜,找出最大的k即可。
复杂度: \(O(n*m)\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <bits/stdc++.h>
using namespace std;
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int n, cnt = 0;
cin >> n;
for (int i = 0; i < n; i++)
{
int v, m, k, ans = -1;
cin >> v >> m;
while (m--)
{
cin >> k;
if (v % (k + 1))
ans = max(ans, k);
}
if (ans != -1)
cnt++;
cout << ans << '\n';
}
cout << (cnt > n - cnt ? "successful" : "I can't help you");
return 0;
}
E 臭袜子
考点:
- STL
题解:
简单题
使用 map
嵌套 pair
进行编号的计数,答案更新的情况有两种,一种是出现的次数比现在的更多,一种是次数相同但是第一次出现的更早。
复杂度: \(O(n)\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <bits/stdc++.h>
using namespace std;
map<pair<int, int>, int> cnt;
map<pair<int, int>, int> t;
pair<int, int> ans;
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
pair<int, int> now;
cin >> now.first >> now.second;
cnt[now]++;
if (t.count(now) == 0)
t[now] = i;
if (cnt[now] > cnt[ans] || cnt[now] == cnt[ans] && t[now] <= t[ans])
ans = now;
}
cout << cnt[ans] << ' ' << ans.first << ' ' << ans.second;
return 0;
}
F 我要上厕所!
考点:
- 模拟
- STL
- 分治
题解:
中等题
解法一:分治 \(O(n\log n)\)
分:将数组分成两半,直到只有一个元素
治:当只有一个元素的时候这个部分的最大就是它本身
合:将两个数组和在一起的时候是哪种情况:
- 最大子数组是其左侧数组的最大子数组
- 最大子数组是其右侧数组的最大子数组
- 最大子数组出现在中间位置:从中间向两侧分别加起来,找到分别的最大值然后求和
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e4 + 10;
int findmid(vector<int> &a, int l, int mid, int r)
{
int left_max = 0;
int left_sum = 0;
for (int i = mid; i >= l; i--)
{
left_sum += a[i];
left_max = max(left_max, left_sum);
}
int right_max = 0;
int right_sum = 0;
for (int i = mid + 1; i <= r; i++)
{
right_sum += a[i];
right_max = max(right_max, right_sum);
}
return left_max + right_max;
}
int find(vector<int> &a, int l, int r)
{
if (l == r)
return a[l];
int mid = (l + r) / 2;
int left_max = find(a, l, mid);
int right_max = find(a, mid + 1, r);
int mid_max = findmid(a, l, mid, r);
return max({mid_max, left_max, right_max});
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int n;
cin >> n;
map<string, bool> out;
vector<int> a(N), b(N);
for (int i = 0; i < n; i++)
{
int k, f, t;
string s;
cin >> k >> f >> s >> t;
if ((k == 1 && s[0] > 'm') || (k == 2 && s[0] < 'n'))
continue;
if (f == 2)
{
out[s] = false;
if (k == 1)
a[t]--;
else
b[t]--;
}
else
{
if (out[s])
continue;
out[s] = true;
if (k == 1)
a[t]++;
else
b[t]++;
}
}
cout << find(a, 0, 50000) << ' ' << find(b, 0, 50000);
return 0;
}
解法二:dp \(O(n)\)
这里对 \(dp\) 数组的定义为 以为第 \(i\) 秒为结尾的最大子数组和为 dp[i]
。
状态转移方程为 \(dp[i] = max(nums[i],\ dp[i - 1] + nums[i])\) ,即如果加上第 \(i\) 秒更优就加,否则重新开始一段子数组。
注意到 dp[i]
仅仅和 dp[i-1]
的状态有关,所以可以直接在原数组操作。
有 \(nums[i] = max(nums[i],\ nums[i - 1] + nums[i])\)
输出其中的最大值即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e4 + 10;
int dp(vector<int> &a)
{
int res = a[0];
for (int i = 1; i <= 50000; i++)
{
a[i] = max(a[i - 1] + a[i], a[i]);
res = max(res, a[i]);
}
return res;
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int n;
cin >> n;
map<string, bool> out;
vector<int> a(N), b(N);
for (int i = 0; i < n; i++)
{
int k, f, t;
string s;
cin >> k >> f >> s >> t;
if ((k == 1 && s[0] > 'm') || (k == 2 && s[0] < 'n'))
continue;
if (f == 2)
{
out[s] = false;
if (k == 1)
a[t]--;
else
b[t]--;
}
else
{
if (out[s])
continue;
out[s] = true;
if (k == 1)
a[t]++;
else
b[t]++;
}
}
cout << dp(a) << ' ' << dp(b);
return 0;
}
G Faker的阿卡丽
考点:
- 贪心
题解:
简单题
我们可以一开始就使用 \(w\) ,增加 \(b\) 点能量和 \(1\) 点帅气值
然后我们将使用技能和普通攻击算作一个整体,即每次消耗 \(min(a,c)-d\) 点能量,增加 \(2\) 点帅气值
但是这样我们需要先把最后一次处理,因为此时的能量并不够我们先用再加,直接记为增加 \(2\) 点帅气值即可。
复杂度: \(O(1)\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <bits/stdc++.h>
using namespace std;
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int n, a, b, c, d;
cin >> n >> a >> b >> c >> d;
n += b;
int t = min(a, c);
n -= t;
cout << 1 + n / (t - d) * 2LL + 2;
return 0;
}
H 挑区间
考点:
- 前缀和
题解:
中等题
对于一段区间 \([l,r]\) \((1 \le l \le r \le n)\) 我们可以预处理出前缀和数组 pre
和后缀和数组 suf
,pre[i]
表示这 \([1,i]\) 之间的累加和, suf[i]
表示 \([i,n]\) 之间的累加和。
我们可以把序列分为三段 \([1,l-1],[l,r],[r+1,n]\) 。其中 \([1,l-1]\) 和 \([r+1,1]\) 我们通过前后缀和查询复杂度为 \(O(1)\)
对于 \([l,r]\) 我们枚举每一个左端点 \(l\) ,遍历所有的可能区间 \([l,r]\) ,复杂度为 \(O(n^2)\) 。需要注意的是边界的情况需要特判。
复杂度: \(O(n^2)\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int n, d;
cin >> n >> d;
vector<int> a(n + 10), b(n + 10);
for (int i = 1; i <= n; i++)
cin >> a[i];
vector<ll> pre(n + 10), suf(n + 10);
for (int i = 2; i <= n; i++)
pre[i] = pre[i - 1] + abs(a[i] - a[i - 1]);
for (int i = n - 1; i >= 1; i--)
suf[i] = suf[i + 1] + abs(a[i] - a[i + 1]);
ll ans = 0;
for (int l = 1; l <= n; l++)
{
b = a;
ll tmp = 0;
for (int r = l; r <= min(l + d - 1, n); r++)
{
b[r] = r;
if (r != 1)
tmp += abs(b[r] - b[r - 1]);
if (r != n)
tmp += abs(b[r] - b[r + 1]);
ans = max(ans, pre[l - 1] + suf[r + 1] + tmp);
if (r != n)
tmp -= abs(b[r] - b[r + 1]);
}
}
cout << ans;
return 0;
}
I 四季
考点:
- 基础语法
题解:
签到题
顺带考察常识:
- \(3,4,5\) 是春天
- \(6,7,8\) 是夏天
- \(9,10,11\) 是秋天
- \(12,1,2\) 是冬天
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
if (n <= 2)
cout << "winter";
else if (n <= 5)
cout << "spring";
else if (n <= 8)
cout << "summer";
else if (n <= 11)
cout << "autumn";
else
cout << "winter";
return 0;
}
J 飞升
考点:
- 二分
题解:
困难题
对每个人的能力值进行二分,找到满足条件的最大值
二分边界为 \([a[i],a[i]+k]\) ,即一次都不用和 \(k\) 次都用在他身上
对于 check()
函数,从当前这位开始,向右边累加构造出阶梯型需要多少次操作。判断需要的次数 \(cnt\) 是否满足 \(cnt\le k\) 即可
复杂度: \(O(n log n)\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 + 10;
vector<int> a(N);
int n, k;
bool check(int x, int i)
{
ll cnt = 0;
for (int p = 0; i + p <= n; p++)
{
if (a[i + p] >= (x - p))
{
if (cnt > k)
return false;
else
return true;
}
cnt += (x - p) - a[i + p];
}
return false;
}
void solve()
{
cin >> n >> k;
int ans = 0;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
{
int l = a[i], r = a[i] + k, tmp;
while (l <= r)
{
int mid = (l + r) / 2;
if (check(mid, i))
tmp = mid, l = mid + 1;
else
r = mid - 1;
}
ans = max(ans, tmp);
}
cout << ans << '\n';
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--)
solve();
return 0;
}
K f(x) = floor(log₂(x))
考点:
- 数学
题解:
中等题
先预处理 \(10^{18}\) 内所有 \(2\) 的指数幂
然后一段一段的累加得到 \(1 \sim l\) 的和,同理得到 \(1 \sim r\) 的和
做差即可得到结果
复杂度: \(O(q \log r)\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
ll v[100] = {1};
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
for (int i = 1; i < 63; i++)
v[i] = v[i - 1] * 2;
int q;
cin >> q;
while (q--)
{
ll l, r;
cin >> l >> r;
ll a = 0, b = 0, p;
p = upper_bound(v, v + 63, l) - v - 1;
for (int i = 1; i < p; i++)
{
a += ((v[i] % mod) * i) % mod;
a %= mod;
}
a += p * (l - v[p]) % mod;
a %= mod;
p = upper_bound(v, v + 63, r) - v - 1;
for (int i = 1; i < p; i++)
{
b += ((v[i] % mod) * i) % mod;
b %= mod;
}
b += p * ((r - v[p] + 1) % mod);
b %= mod;
cout << (b - a + mod) % mod << '\n';
}
return 0;
}
L 哪个倒霉蛋还得再体测一次
考点:
- 递推
题解:
简单题
解法一:递推
易知: \(a[n]= \sum_{i=1}^{n/2}a[i]+1\)
复杂度: \(O(n^2)\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <bits/stdc++.h>
using namespace std;
int a[1010];
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= i / 2; j++)
a[i] += a[j];
a[i]++;
}
cout << a[n];
return 0;
}
解法二:递归+记忆化
将递归的结果先存起来,下次遇到就不用再递归下去
复杂度: \(O(n \log n)\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <bits/stdc++.h>
using namespace std;
int a[1010];
int func(int n)
{
if (n == 1)
return 1;
if (a[n])
return a[n];
int sum = 1;
for (int i = 1; i <= n / 2; ++i)
sum += func(i);
return a[n] = sum;
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int n;
cin >> n;
cout << func(n);
return 0;
}
解法三:打表
以下是打表的代码
做表大概十分钟,打完表复杂度为 \(O(1)\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits/stdc++.h>
using namespace std;
int func(int n)
{
if (n == 1)
return 1;
int sum = 1;
for (int i = 1; i <= n / 2; i++)
sum += func(i);
return sum;
}
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
cout << "int a[1010]={0";
for (int i = 1; i <= 1000; i++)
cout << "," << func(i);
cout << "};";
return 0;
}
M 差分是你的谎言O.o
考点:
- 基础语法
题解:
签到题
按照题意,用 for
构造出 \(b\) 数组即可
复杂度: \(O(n)\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <bits/stdc++.h>
using namespace std;
int main()
{
cin.tie(0)->ios::sync_with_stdio(false);
int n;
cin >> n;
vector<int> a(n), b(n - 1);
for (int i = 0; i < n; i++)
cin >> a[i];
for (int i = 0; i < n - 1; i++)
b[i] = abs(a[i + 1] - a[i]);
for (int i = 0; i < n - 1; i++)
{
if (i)
cout << ' ';
cout << b[i];
}
return 0;
}