Skip to content

Commit 686c3a5

Browse files
Update
1 parent bcbd631 commit 686c3a5

9 files changed

+466
-48
lines changed

problems/kamacoder/0058.区间和.md

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
2+
# 58. 区间和
3+
4+
[题目链接](https://kamacoder.com/problempage.php?pid=1070)
5+
6+
题目描述
7+
8+
给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。
9+
10+
输入描述
11+
12+
第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间,直至文件结束。
13+
14+
输出描述
15+
16+
输出每个指定区间内元素的总和。
17+
18+
输入示例
19+
20+
```
21+
5
22+
1
23+
2
24+
3
25+
4
26+
5
27+
0 1
28+
1 3
29+
```
30+
31+
输出示例
32+
33+
```
34+
3
35+
9
36+
```
37+
38+
数据范围:
39+
40+
0 < n <= 100000
41+
42+
## 思路
43+
44+
本题我们来讲解 数组 上常用的解题技巧:前缀和
45+
46+
首先来看本题,我们最直观的想法是什么?
47+
48+
那就是给一个区间,然后 把这个区间的和都累加一遍不就得了,是一道简单不能再简单的题目。
49+
50+
代码如下:
51+
52+
```CPP
53+
#include <iostream>
54+
#include <vector>
55+
using namespace std;
56+
int main() {
57+
int n, a, b;
58+
cin >> n;
59+
vector<int> vec(n);
60+
for (int i = 0; i < n; i++) cin >> vec[i];
61+
while (cin >> a >> b) {
62+
int sum = 0;
63+
// 累加区间 a 到 b 的和
64+
for (int i = a; i <= b; i++) sum += vec[i];
65+
cout << sum << endl;
66+
}
67+
}
68+
```
69+
70+
代码一提交,发现超时了.....
71+
72+
我在制作本题的时候,特别制作了大数据量查询,卡的就是这种暴力解法。
73+
74+
来举一个极端的例子,如果我查询m次,每次查询的范围都是从0 到 n - 1
75+
76+
那么该算法的时间复杂度是 O(n * m) m 是查询的次数
77+
78+
如果查询次数非常大的话,这个时间复杂度也是非常大的。
79+
80+
接下来我们来引入前缀和,看看前缀和如何解决这个问题。
81+
82+
前缀和的思想是重复利用计算过的子数组之和,从而降低区间查询需要累加计算的次数。
83+
84+
**前缀和 在涉及计算区间和的问题时非常有用**
85+
86+
前缀和的思路其实很简单,我给大家举个例子很容易就懂了。
87+
88+
例如,我们要统计 vec[i] 这个数组上的区间和。
89+
90+
我们先做累加,即 p[i] 表示 下标 0 到 i 的 vec[i] 累加 之和。
91+
92+
如图:
93+
94+
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240627110604.png)
95+
96+
97+
如果,我们想统计,在vec数组上 下标 2 到下标 5 之间的累加和,那是不是就用 p[5] - p[1] 就可以了。
98+
99+
为什么呢?
100+
101+
p[1] = vec[0] + vec[1];
102+
103+
p[5] = vec[0] + vec[1] + vec[2] + vec[3] + vec[4] + vec[5];
104+
105+
p[5] - p[1] = vec[2] + vec[3] + vec[4] + vec[5];
106+
107+
这不就是我们要求的 下标 2 到下标 5 之间的累加和吗。
108+
109+
如图所示:
110+
111+
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240627111319.png)
112+
113+
p[5] - p[1] 就是 红色部分的区间和。
114+
115+
而 p 数组是我们之前就计算好的累加和,所以后面每次求区间和的之后 我们只需要 O(1)的操作。
116+
117+
118+
119+
```CPP
120+
#include <iostream>
121+
#include <vector>
122+
using namespace std;
123+
int main() {
124+
int n, a, b;
125+
cin >> n;
126+
vector<int> vec(n);
127+
vector<int> p(n);
128+
int presum = 0;
129+
for (int i = 0; i < n; i++) {
130+
cin >> vec[i];
131+
presum += vec[i];
132+
p[i] = presum;
133+
}
134+
135+
while (cin >> a >> b) {
136+
int sum;
137+
if (a == 0) sum = p[b];
138+
else sum = p[b] - p[a - 1];
139+
cout << sum << endl;
140+
}
141+
}
142+
143+
```
144+
145+
```CPP
146+
#include <iostream>
147+
#include <vector>
148+
using namespace std;
149+
int main() {
150+
int n, a, b;
151+
cin >> n;
152+
vector<int> vec(n);
153+
vector<int> p(n);
154+
int presum = 0;
155+
for (int i = 0; i < n; i++) {
156+
scanf("%d", &vec[i]);
157+
presum += vec[i];
158+
p[i] = presum;
159+
}
160+
161+
while (~scanf("%d%d", &a, &b)) {
162+
int sum;
163+
if (a == 0) sum = p[b];
164+
else sum = p[b] - p[a - 1];
165+
printf("%d\n", sum);
166+
}
167+
}
168+
169+
```
170+
171+
```CPP
172+
173+
#include<bits/stdc++.h>
174+
using namespace std;
175+
176+
177+
int main(){
178+
int n, a, b;
179+
cin >> n;
180+
vector<int> vec(n + 1);
181+
vector<int> p(n + 1, 0);
182+
for(int i = 1; i <= n; i++) {
183+
scanf("%d", &vec[i]);
184+
p[i] = p[i - 1] + vec[i];
185+
}
186+
while(~scanf("%d%d", &a, &b)){
187+
printf("%d\n", p[b + 1] - p[a]);
188+
}
189+
return 0;
190+
}
191+
```

problems/kamacoder/0105.有向图的完全可达性.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
1 2
2525
2 1
2626
1 3
27-
3 4
27+
2 4
2828
```
2929

3030
【输出示例】

problems/kamacoder/0126.骑士的攻击astar.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
在象棋中,马和象的移动规则分别是“马走日”和“象走田”。现给定骑士的起始坐标和目标坐标,要求根据骑士的移动规则,计算从起点到达目标点所需的最短步数。
99

10+
骑士移动规则如图,红色是起始位置,黄色是骑士可以走的地方。
11+
12+
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240626104833.png)
13+
1014
棋盘大小 1000 x 1000(棋盘的 x 和 y 坐标均在 [1, 1000] 区间内,包含边界)
1115

1216
输入描述
@@ -42,6 +46,7 @@
4246
0
4347
```
4448

49+
4550
## 思路
4651

4752
我们看到这道题目的第一个想法就是广搜,这也是最经典的广搜类型题目。

problems/kamacoder/0132.夹吃旗.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
2+
# 132. 夹吃棋
3+
4+
[题目链接](https://kamacoder.com/problempage.php?pid=1209)
5+
6+
这道题是模拟题,但很多录友可能想复杂了。
7+
8+
行方向,白棋吃,只有这样的布局 `o*o`,黑棋吃,只有这样的布局 `*o*`
9+
10+
列方向也是同理的。
11+
12+
想到这一点,本题的代码就容易写了, C++代码如下:
13+
14+
```CPP
15+
#include <iostream>
16+
#include <vector>
17+
using namespace std;
18+
int main() {
19+
int n;
20+
cin >> n;
21+
while (n--) {
22+
int black = 0, white = 0;
23+
vector<string> grid(3, "");
24+
// 判断行
25+
for (int i = 0; i < 3; i++) {
26+
cin >> grid[i];
27+
if (grid[i] == "o*o") white++;
28+
if (grid[i] == "*o*") black++;
29+
}
30+
// 判断列
31+
for (int i = 0; i < 3; i++) {
32+
string s;
33+
s += grid[0][i];
34+
s += grid[1][i];
35+
s += grid[2][i];
36+
if (s == "o*o") white++;
37+
if (s == "*o*") black++;
38+
}
39+
// 如果一个棋盘的局面没有一方被夹吃或者黑白双方都被对面夹吃,则认为是平局
40+
if ((!white && !black) || (white && black)) cout << "draw" << endl;
41+
// 白棋赢
42+
else if (white && !black) cout << "yukan" << endl;
43+
// 黑棋赢
44+
else cout << "kou" << endl;
45+
}
46+
}
47+
```
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
2+
# 133. 小红买药
3+
4+
[题目链接](https://kamacoder.com/problempage.php?pid=1210)
5+
6+
本题是一道直观的模拟题,但也并不简单,很多情况容易漏了,笔试现场可能要多错几次 才能把情况都想到。
7+
8+
主要是三个情况:
9+
10+
* 小红没症状,药有副作用,统计加一,同时要给小红标记上症状
11+
* 小红有症状,药治不了,同时也没副症状 ,这时也要统计加一
12+
* 小红有症状,药可以治,给小红取消症状标记
13+
14+
15+
```CPP
16+
#include <iostream>
17+
#include <vector>
18+
using namespace std;
19+
int main() {
20+
int n, m, q, u;
21+
cin >> n;
22+
string s;
23+
cin >> s;
24+
cin >> m;
25+
vector<string> a(m + 1); // 因为后面u是从1开始的
26+
vector<string> b(m + 1);
27+
for (int i = 1; i <= m; i++) {
28+
cin >> a[i] >> b[i];
29+
}
30+
cin >> q;
31+
while (q--) {
32+
cin >> u;
33+
int num = 0;
34+
for (int i = 0; i < n; i++) {
35+
// s 没症状,但b给了副作用,统计num的同时,要给s标记上症状
36+
if (s[i] == '0' && b[u][i] == '1') {
37+
num ++;
38+
s[i] = '1';
39+
}
40+
// s 有症状,但 a治不了,b也没副症状
41+
else if (s[i] == '1' && a[u][i] == '0' && a[u][i] == '0') num++;
42+
// s 有症状,a 可以治
43+
else if (s[i] == '1' && a[u][i] == '1') s[i] = '0';
44+
}
45+
cout << num << endl;
46+
}
47+
}
48+
```
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
2+
# 134. 皇后移动的最小步数
3+
4+
[题目链接](https://kamacoder.com/problempage.php?pid=1211)
5+
6+
本题和 [代码随想录-不同路径](https://www.programmercarl.com/0062.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84.html) 有一些类似。
7+
8+
关键是弄清楚递推公式
9+
10+
一共分三个情况,
11+
12+
情况一,向右移动:
13+
14+
然后从 (i, j) 再向右走 到 (i, k)。 无论k 多大,步数只加1 :
15+
16+
`dp[i][k] = dp[i][j] + 1`
17+
18+
那么 `dp[i][k]` 也有可能 从其他方向得到,例如 从上到下, 或者斜上方到达 dp[i][k]
19+
20+
本题我们要求最小步数,所以取最小值:`dp[i][k] = min(dp[i][k], dp[i][j] + 1);`
21+
22+
情况二,向下移动:
23+
24+
从 (i, j) 再向下走 到 (k, j)。 无论k 多大,步数只加1 :
25+
26+
`dp[k][j] = dp[i][j] + 1;`
27+
28+
同理 `dp[i][k]` 也有可能 从其他方向得到,取最小值:`dp[k][j] = min(dp[k][j], dp[i][j] + 1);`
29+
30+
情况三,右下方移动:
31+
32+
从 (i, j) 再向右下方移动 到 (i + k, j + k)。 无论k 多大,步数只加1 :
33+
34+
`dp[i + k][j + k] = dp[i][j] + 1`
35+
36+
同理 `dp[i + k][j + k]` 也有可能 从其他方向得到,取最小值:`dp[i + k][j + k] = min(dp[i + k][j + k], dp[i][j] + 1);`
37+
38+
39+
```CPP
40+
#include <iostream>
41+
#include <vector>
42+
using namespace std;
43+
const int INF = 4e6; // 最多步数也就是 2000 * 2000
44+
int main() {
45+
int n, m;
46+
cin >> n >> m;
47+
vector<vector<char>> grid(n, vector<char>(m));
48+
for (int i = 0; i < n; i++) {
49+
for (int j = 0; j < m; j++) {
50+
cin >> grid[i][j];
51+
}
52+
}
53+
vector<vector<int>> dp(n, vector<int>(m, INF));
54+
dp[0][0] = 0;
55+
for (int i = 0; i < n; i++) {
56+
for (int j = 0; j < m; j++) {
57+
if (grid[i][j] == '*') continue;
58+
// 向右移动k个格子
59+
for (int k = j + 1; k < m && grid[i][k] == '.'; k++) {
60+
dp[i][k] = min(dp[i][k], dp[i][j] + 1);
61+
}
62+
// 向下移动 k个格子
63+
for (int k = i + 1; k < n && grid[k][j] == '.'; k++) {
64+
dp[k][j] = min(dp[k][j], dp[i][j] + 1);
65+
}
66+
// 向右下移动k个格子
67+
for (int k = 1; i + k < n && j + k < m && grid[i + k][j + k] == '.'; k++) {
68+
dp[i + k][j + k] = min(dp[i + k][j + k], dp[i][j] + 1);
69+
}
70+
}
71+
}
72+
if (dp[n - 1][m - 1] == INF) cout << -1 << endl;
73+
else cout << dp[n - 1][m - 1] << endl;
74+
}
75+
```
76+
77+

0 commit comments

Comments
 (0)