【基础完全搜索】USACO Bronze 2020 December - 雏菊链Daisy Chains
题目描述
每天散步时,小母牛 Bessie 都会去她最喜欢的牧场转一圈。
牧场上有 NNN 朵花,这些花是一排五彩缤纷的雏菊,编号为 111 到 NNN,第 iii 朵花有 pip_ipi 个花瓣。
Bessie 是一位正在学习摄影的摄影师。她决定拍摄很多花朵的照片。具体来说,对于每一对 (i,j)(i, j)(i,j) 满足 1≤i≤j≤N1 \le i \le j \le N1≤i≤j≤N,Bessie 都会拍摄从第 iii 朵花到第 jjj 朵花(包含两端)的一张照片。
拍完照之后,Bessie 发现有些照片中存在所谓的 “平均花” —— 即:照片中存在一朵花,其花瓣数恰好等于这张照片中所有花的平均花瓣数。
请你计算:Bessie 总共拍摄了多少张包含“平均花”的照片?
输入格式
输入共两行:
- 第一行一个整数 NNN(1≤N≤1001 \le N \le 1001≤N≤100),表示花朵的数量。
- 第二行 NNN 个整数 p1,p2,…,pNp_1, p_2, \dots, p_Np1,p2,…,pN(1≤pi≤10001 \le p_i \le 10001≤pi≤1000),表示每朵花的花瓣数。
输出格式
输出一个整数,表示包含“平均花”的照片总数。
样例输入
4
1 1 2 3
样例输出
6
样例解释
所有单独一朵花的照片一定满足条件(有 444 张)。
另外还有:
- (1,2)(1,2)(1,2):花瓣为 [1,1][1,1][1,1],平均值为 111,包含一朵等于平均值的花(两朵都是)
- (2,4)(2,4)(2,4):花瓣为 [1,2,3][1,2,3][1,2,3],平均值为 222,包含一朵正好为 222 的花
因此总共有 6 张照片满足条件。
提交链接
Daisy Chains
思路分析
🧠 算法思路概览
-
使用前缀和数组快速计算任意区间 [i,j][i, j][i,j] 的花瓣总数;
-
枚举所有 [i,j][i, j][i,j] 区间;
-
对于每个区间,计算其平均花瓣数;
-
在区间中检查是否有花瓣数正好等于平均值;
-
如果存在,计数器加一。
vector<int> p(n + 1), sum(n + 1);
for (int i = 1; i <= n; i++)
{cin >> p[i];sum[i] = sum[i - 1] + p[i]; // 前缀和数组
}
定义两个数组:
-
p[i]p[i]p[i]:表示第 iii 朵花的花瓣数;
-
sum[i]:p[1] + p[2] + ... + p[i]
的前缀和,用于快速计算区间和。
这样,我们可以在 O(1)O(1)O(1) 时间内求任意区间 [i,j][i, j][i,j] 的总和为 sum[j] - sum[i - 1]
。
for (int i = 1; i <= n; i++)for (int j = i; j <= n; j++)
枚举所有区间 [i,j][i, j][i,j],总共 O(n2)O(n^2)O(n2) 个区间。
double ave = (sum[j] - sum[i - 1]) * 1.0 / (j - i + 1); //[i,j]范围的平均值
计算区间 [i,j][i, j][i,j] 的平均值。总和是:sum[j] - sum[i - 1]
,区间长度是:j - i + 1
bool check = false;
for (int k = i; k <= j; k++)
{if (p[k] == ave)check = true;
}
遍历区间 [i,j][i, j][i,j] 内的每一朵花:检查是否有某一朵花的花瓣数刚好等于平均值。
如果找到了,就将 checkcheckcheck 设为 truetruetrue。
⏱️ 时间复杂度分析
-
外层两重循环 O(n2)O(n^2)O(n2);
-
内层判断 O(n)O(n)O(n);
-
总复杂度:O(n3)O(n^3)O(n3),但实际最多 100 × 100 × 100 = 10610^6106,可以接受。
参考代码
#include <bits/stdc++.h>
using namespace std;
int main()
{int n;cin >> n;vector<int>p(n + 1) , sum(n + 1);for(int i = 1; i <= n; i++){cin >> p[i];sum[i] = sum[i - 1] + p[i]; //前缀和数组}int cnt = 0;//枚举(i,j)区间的所有可能的情况for(int i = 1; i <= n; i++){for(int j = i; j <= n; j++){double ave = (sum[j] - sum[i - 1]) * 1.0 / (j - i + 1); //[i,j]范围的平均值bool check = false;for(int k = i; k <= j; k++){if(p[k] == ave)check = true;}cnt += check;}}cout << cnt;return 0;
}