EOJ刷题记录第四弹

Problem #3369 - ECNU Online Judge

注意边数,意识到了使用邻接表,但是边数是二倍忘记考虑

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
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1010;
int e[2 * N], ne[2 * N], h[N], idx;
int n;
int u, v;
long long ans;

void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void dfs(int root, int fa, int res)
{
if(res >= 4) {
ans ++;
return;
}

for(int i = h[root]; i != -1; i = ne[i])
{
int j = e[i];
if(j != fa)
dfs(j, root, res + 1);
}
}

int main()
{
memset(h, -1, sizeof h);
cin >> n;

for(int i = 1; i <= n - 1; i ++)
{
cin >> u >> v;
add(u, v);
add(v, u);
}

for(int i = 1; i <= n; i ++)
dfs(i, -1, 1);

cout << ans / 2 << endl;

return 0;
}

Problem #3367 - ECNU Online Judge

第一眼是前缀和-差分数组问题,维护一个数组chazhi[N],定义为0-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
// 错误思路,TLE
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010;
int sum0[N];
int sum1[N];
int n;

int main()
{
cin >> n;
int t;
for(int i = 1; i <= n; i ++)
{
cin >> t;
sum1[i] = sum1[i - 1];
sum0[i] = sum0[i - 1];
if(t == 1)
sum1[i] ++;
else
sum0[i] ++;
}

int ans = 0;
for(int i = 1; i <= n; i ++)
for(int j = i; j <= n; j ++)
ans = max(ans, sum0[j] - sum0[i - 1] - (sum1[j] - sum1[i - 1]));

cout << ans + sum1[n] << endl;
return 0;
}

最大子数组和 有一个非常经典的 O(n) 算法,叫做 Kadane’s Algorithm ( Kadane算法 )

  1. Kadane 算法要解决什么问题? (很明显,就是最大子数组和问题)
  2. Kadane 算法的核心思想是什么? (动态规划?贪心? 仔细体会它的递推关系)
  3. Kadane 算法的时间复杂度是多少? (O(n),非常高效)
  4. 如何用代码实现 Kadane 算法? (C/C++ 代码并不复杂)

学习 Kadane 算法之后,你就会发现,它可以非常漂亮地解决我们当前的问题。 只需要把原始数组转换成 [1, -1, -1, 1, 1, 1, …] 这种形式,然后用 Kadane 算法求最大子数组和,再加上原始数组中 1 的数量,就是最终答案!

3. 算法步骤 (Step-by-Step Logic)

假设输入数组为 arr,长度为 n。

  1. 初始化:
  • global_maximum = arr[0] (或者负无穷,如果数组可能全为负数)
  • current_maximum = arr[0] (或者 0,如果允许空子数组,但本题要求至少翻转一条咸鱼,所以子数组不能为空,这里初始化为第一个元素更合适)
  1. 迭代数组 (从第二个元素开始,索引从 1 到 n-1):
  • 对于每个元素 arr[i]:
    • current_maximum = max(arr[i], current_maximum + arr[i]) // 关键步骤:更新 current_maximum
    • global_maximum = max(global_maximum, current_maximum) // 更新 global_maximum
  1. 返回 global_maximum

推荐题目列表:

  1. 题目名称: 最大子矩阵和 (Maximum Submatrix Sum)
    • 简要描述: 给定一个二维矩阵,找到一个子矩阵,使其元素之和最大。
    • 解题思路提示: 可以将二维问题降维到一维,枚举子矩阵的上下边界,然后将每一列的子矩阵部分求和,转化为一维的最大子数组和问题。 可以使用 Kadane’s Algorithm 解决一维子问题。
    • 预期学习效果: 掌握将一维 DP 思想扩展到二维问题的技巧,加深对 Kadane’s Algorithm 的理解。
  2. 题目名称: 环形子数组最大和 (Maximum Sum Circular Subarray)
    • 简要描述: 给定一个环形数组,找到一个子数组,使其元素之和最大。环形数组意味着数组的最后一个元素和第一个元素是相邻的。
    • 解题思路提示: 环形数组的最大子数组和,要么是普通子数组的最大和 (不跨越环形边界),要么是跨越环形边界的子数组和。 可以分别计算这两种情况,取最大值。 跨越环形边界的子数组和,可以转换为 数组总和 减去 最小子数组和。
    • 预期学习效果: 学习如何处理环形数组问题,掌握将问题分解为不同情况进行讨论的技巧。
  3. 题目名称: 乘积最大子数组 (Maximum Product Subarray)
    • 简要描述: 给定一个整数数组,找到一个非空的连续子数组,使其乘积最大。
    • 解题思路提示: 与最大子数组和类似,但需要同时维护最大值和最小值。 因为负数的存在,最小值乘以负数可能变成最大值。 状态转移时需要考虑当前元素、之前的最大值和最小值。
    • 预期学习效果: 学习如何处理乘积最大化问题,理解负数对最大值的影响,扩展动态规划的应用场景。

用DP考虑

步骤

  • 状态怎么表示
  • 状态怎么转移
  • dp数组怎么初始化

Comments