子三角形中最大元素的总和
Posted
技术标签:
【中文标题】子三角形中最大元素的总和【英文标题】:Sum of max elements in sub-triangles 【发布时间】:2019-03-20 04:53:15 【问题描述】:我在今年的 CCC 2019 S5 上尝试了这个last question。
问题陈述:
在平行宇宙中,计算机科学中最重要的数据结构是三角形。大小为 M 的三角形由 M 行组成,第 i 行包含 i 个元素。此外,这些行必须排列成等边三角形的形状。也就是说,每一行都以通过三角形中间的垂直对称线为中心。例如,下图显示了一个大小为 4 的三角形:
三角形包含子三角形。例如,上面的三角形包含十个大小为 1 的子三角形,六个大小为 2 的子三角形(其中两个是包含 (3,1,2) 的三角形和包含 (4,6,1) 的三角形),三个大小为 3 的子三角形(其中一个包含 (2,2,1,1,4,2))。请注意,每个三角形都是其自身的子三角形。
给定一个大小为 N 的三角形,必须找到大小为 K 的每个子三角形的最大元素之和。
输入规范
第一行包含两个空格分隔的整数 N 和 K (1≤K≤N≤3000)。
接下来是描述三角形的 N 行。这些行的第 i 个包含 i 个空格分隔的整数 ai,j (0≤ai,j≤10^9),表示三角形的第 i 行。
对于 15 个可用标记中的 4 个,N≤1000。
输出规格
输出每个大小为 K 的子三角形的最大元素的整数和。
示例输入
4 2
3
1 2
4 2 1
6 1 4 2
样本输入的输出
23
很遗憾,我的解决方案给出了 TLE 判定,我不知道如何优化它。
这个问题基本上要求找到子三角形的最大元素并将它们加在一起。我的方法很简单,我迭代大三角形的每个元素,使它们成为子三角形的“根”,然后我尝试去它的每个元素找到最大值并将它们添加到结果中。
我需要一些帮助来改进我的解决方案,它需要一些数据结构吗?
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
ios_base::sync_with_stdio(false);
cin.tie(NULL);
vector<vector<int>> triangle;
int n;
int k;
cin >> n >> k;
for (int i = 0; i < n; ++i)
triangle.push_back(vector<int>(i + 1, 0));
for (int j = 0; j <= i; ++j)
cin >> triangle[i][j];
int sum = 0;
for (int level = 0; level <= n - k; ++level)
for (int root = 0, size1 = triangle[level].size(); root < size1; ++root)
int largest = 0;
for (int i = level, counter = 0; i < level + k; ++i, ++counter)
for (int j = root, times = 0, size2 = triangle[i].size(); j < size2 && times <= counter; ++j, ++times)
largest = max(largest, triangle[i][j]);
sum += largest;
cout << sum << "\n";
return 0;
【问题讨论】:
【参考方案1】:这是一个足够快的解决方案O(n^2 log(k))
。
想法是这样的。从大小为 1 的三角形的 nxn
三角形到大小为 2 的三角形的最大值的 (n-1)x(n-1)
三角形是 O(n)
操作。只需将每个三角形与其相邻三角形的最大值进行比较。
可以使用相同的技巧从第二个三角形转到大小为 2 的三角形的 (n-2)x(n-2)
三角形。但是,如果您在每个方向上跳过一个,则可以直接到达最大值的 (n-3)x(n-3)
三角形在大小为 4 的三角形中。也及时O(n)
。为了说明后者,假设我们从以下开始:
2
3 1
1 2 4
4 2 1 5
6 1 4 2 3
为了得到大小为 2 的三角形,我们将每个三角形与其相邻的三角形进行比较。
3
3 4
4 2 5
6 4 4 5
为了得到大小为 4 的三角形比较跳过一个,所以我们比较底部的一个 6、3、4。下一个我们比较 4、4、5 等等。获取:
5
6 5
然后我们将它们加在一起得到 11。
接下来,从(n-3)x(n-3)
大小为 4 的三角形中最大值的三角形中,您可以通过选择三角形的大小直接转到大小为 5、6、7 或 8 的三角形中的最大值三角形'会比较下一个,跳过1,跳过2或跳过3。
依此类推,导致O(log(k))
步骤通过k
三角形获得k
中最大值的三角形。
【讨论】:
+1 但我不能完全遵循跳过的想法。您是否暗示不需要查看某些单元格值? (怎么可能?) @גלעדברקן 这是因为您正在查看的单元格之间的单元格包含最大的集合,这些集合位于对您正在查看的值做出贡献的集合的交叉点中。因此,他们可能拥有的有关总体最大值的任何信息都已包含在您正在查看的信息中。但这个推理只适用于你上次的规模翻倍。 我所说的“集合的交集”是指“集合的并集”。哦。 我来晚了,但我很漂亮,这在数学上是不合理的。例如,如果位置 (2, 1) 的数字是 9 而不是 2,这将不起作用。【参考方案2】:这是我的解决方案:
#include <iostream>
#include <vector>
using namespace std;
int max3(int a, int b, int c)
return max(a, max(b,c));
int main()
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n,k;
cin>>n>>k;
vector<vector<long long>> triangle;
for(int i = 0; i < n; i++)
triangle.push_back(vector<long long>(i + 1, 0));
for (int j = 0; j <= i; j++)
cin >> triangle[i][j];
for(int size = 2; size <= k; size++)
for(int i = 0;i <= n - size; i++)
for(int j = 0; j <= i; j++)
triangle[i][j] = max3(triangle[i][j], triangle[i+1][j+1], triangle[i+1][j]);
long long sum = 0;
for(int i = 0;i <= n - k; i++)
for(int j = 0; j <= i; j++)
sum += triangle[i][j];
cout<<sum;
说明:
我将三角形的最上面的正方形称为三角形的顶部。
可能t(i, j, k)
是三角形中最大的数字,顶部为i, j
,大小为k
。
由此我们观察到以下事实:
t(i, j, 1) = triangle[i][j]
每个顶部为(i, j)
、大小为k>=2
的三角形可以通过将其他三个大小为k-1
、顶部为(i, j)
、(i+1, j)
和(i+1, j+1)
的三角形联合起来构成
因此,顶部为(i, j)
、大小为k>=2
的三角形中的最大数将是这三个三角形的最大数中的最大值或写为公式:
t(i, j, k) = max( t(i, j, k-1), t(i+1, j, k-1), t(i, j+1, k-1) )
所以我们需要做的就是为之前的k
存储三角形的最大值。由于我们将从顶部开始迭代三角形,因此我们可以覆盖当前在其中的值,因为该公式仅使用其下方的三角形及其自身来计算新 k
的值。程序应该从size = 2
开始,并使用旧值计算直到k
的所有尺寸的新值。我也使用long long
,因为安全总比抱歉好。希望这会有所帮助!
【讨论】:
这不是 Theta(n^2 k) 时间吗?如果是这样,那似乎太慢了。 @DavidEisenstat 对于1≤K≤N≤3000
的值,它几乎是即时的
能否在您的代码中添加一个带有随机数的示例?以上是关于子三角形中最大元素的总和的主要内容,如果未能解决你的问题,请参考以下文章