子三角形中的最大元素总数

时间:2019-03-20 04:53:16

标签: c++ algorithm optimization

我尝试了今年的CCC 2019 S5中的last question

问题陈述:

在平行宇宙中,计算机科学中最重要的数据结构是三角形。大小为M的三角形由M行组成,第i行包含i个元素。此外,这些行必须布置成形成等边三角形的形状。也就是说,每一行都以穿过三角形中间的垂直对称线为中心。例如,下图显示了一个大小为4的三角形:

enter image description here

一个三角形包含子三角形。例如,上面的三角形包含10个大小为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裁决,而且我不知道如何优化它。

该问题基本上要求找到子三角形的最大元素并将它们加在一起。我的方法很简单,我迭代了大三角形的每个元素,使它们成为子三角形的“根”,然后尝试转到每个三角形的元素以找到max并将它们添加到结果中。

我需要改善我的解决方案的帮助,它需要一些数据结构吗?

#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;
}

2 个答案:

答案 0 :(得分:2)

这是可以快速实现的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。

接下来,通过选择大小为4的三角形中的(n-3)x(n-3)最大值三角形,可以直接选择大小为5、6、7或8的三角形中的最大值三角形。我们将比较下一个,跳过1,跳过2或跳过3。

依次类推,依次进行O(log(k))个步骤,以k个三角形得到k中最大值的三角形。

答案 1 :(得分:1)

这是我的解决方法:

#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),{{1 }}和(i+1, j)
  • 因此,三角形(i+1, j+1)中顶部(i, j)处的三角形中的最大数字将是这3个三角形的最大数目中的最大值或写为公式:
  • 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,因为安全比后悔好。希望这会有所帮助!