洛谷题单指南-分治与倍增-P6648 [CCC2019] Triangle: The Data Structure

原题链接:https://www.luogu.com.cn/problem/P6648

题意解读:在一个n行的数字三角形中,求所有边长为k的正三角形最大值之和。

解题思路:

1、枚举法

枚举每一个边长为k的三角形,在其中求max,然后累加,n最多3000,时间复杂度是n^4,显然超时。

2、倍增和ST思想

此题非常类似于RMQ问题,也就是求区间最值,只不过区间是一个三角形

可否借助ST思想?

不妨设st[i][j][k]表示从(i,j)开始,大小为2^k的三角形内最大值,a[i][j]表示三角形(i,j)位置的值

初始化:st[i][j][0] = a[i][j]

递推:

求值:

假设要计算(i,j)为顶点,边长为k的三角形内最大值

想到这里,兴匆匆的写出如下代码:

0分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 3005;
int a[N][N];
int st[N][N][12]; //st[i][j][k]表示从(i,j)开始,大小为2^k的三角形内最大值
int n, k;
long long ans;

int main()
{
    cin >> n >> k;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= i; j++)
        {
            cin >> a[i][j];
            st[i][j][0] = a[i][j];
        }
    }

    for(int l = 1; l < log2(n); l++)
    {
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                st[i][j][l] = st[i][j][l - 1];
                int x = i + (1 << l - 1);
                for(int y = j; y <= j + (1 << l - 1); y++)
                {
                    st[i][j][l] = max(st[i][j][l], st[x][y][l - 1]);
                }
            }
        }
    }

    for(int i = 1; i + k - 1 <= n; i++)
    {
        for(int j = 1; j <= i; j++)
        {
            int len = log2(k);
            int maxijk = st[i][j][len];
            int x = i + k - (1 << len);
            for(int y = j; y <= j + k - (1 << len); y++)
            {
                maxijk = max(maxijk, st[x][y][len]);
            }
            ans += maxijk;
        }
    }
    cout << ans;

    return 0;
}

原来是int st[N][N][12]爆内存了,需要进一步优化,递推式中l只依赖l-1,因此可以用滚动数组进行优化,于是有:

25分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 3005;
int a[N][N];
int st[N][N][2]; //st[i][j][k]表示从(i,j)开始,大小为2^k的三角形内最大值,第三维用滚动数组优化
int n, k;
long long ans;

int main()
{
    cin >> n >> k;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= i; j++)
        {
            cin >> a[i][j];
            st[i][j][0] = a[i][j];
        }
    }

    for(int l = 1; l <= log2(k); l++)
    {
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                st[i][j][l % 2] = st[i][j][(l - 1) % 2];
                int x = i + (1 << l - 1);
                for(int y = j; y <= j + (1 << l - 1); y++)
                {
                    st[i][j][l % 2] = max(st[i][j][l % 2], st[x][y][(l - 1) % 2]);
                }
            }
        }
    }

    for(int i = 1; i + k - 1 <= n; i++)
    {
        for(int j = 1; j <= i; j++)
        {
            int len = log2(k);
            int maxijk = st[i][j][len % 2];
            int x = i + k - (1 << len);
            for(int y = j; y <= j + k - (1 << len); y++)
            {
                maxijk = max(maxijk, st[x][y][len % 2]);
            }
            ans += maxijk;
        }
    }
    cout << ans;

    return 0;
}

究其原因,关键代码的时间复杂度是logk*N^3,需要进一步优化时间

观察代码:

红色框中是要计算从j开始,窗口长度(1<<l-1)+ 1的最大值,显然可以用单调队列优化,于是就有了以下最终版代码:

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 3005;
int a[N][N];
int st[N][N][2]; //st[i][j][k]表示从(i,j)开始,大小为2^k的三角形内最大值,第三维用滚动数组优化
int n, k;
long long ans;
int q[N], head, tail;

int main()
{
    cin >> n >> k;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= i; j++)
        {
            cin >> a[i][j];
            st[i][j][0] = a[i][j];
        }
    }

    for(int l = 1; l <= log2(k); l++)
    {
        for(int i = 1; i <= n; i++)
        {
            head = 1, tail = 0; //单调队列头、尾指针初始化
            int len = (1 << l - 1) + 1; //窗口大小
            int x = i + (1 << l - 1); //起始横坐标
            for(int j = 1; j <= n; j++)
            {
                st[i][j][l % 2] = st[i][j][(l - 1) % 2];
                //去头
                while(head <= tail && j - q[head] + 1 > len) head++;
                //去尾
                while(head <= tail && st[x][j][(l - 1) % 2] > st[x][q[tail]][(l - 1) % 2]) tail--;
                //存入
                q[++tail] = j;   
                //取窗口最大值,并与i,j - len + 1位置对应的值取max
                if(j >= len)
                    st[i][j - len + 1][l % 2] = max(st[i][j - len + 1][l % 2], st[x][q[head]][(l - 1) % 2]);
            }
        }
    }

    for(int i = 1; i + k - 1 <= n; i++)
    {
        for(int j = 1; j <= i; j++)
        {
            int len = log2(k);
            int maxijk = st[i][j][len % 2];
            int x = i + k - (1 << len);
            for(int y = j; y <= j + k - (1 << len); y++)
            {
                maxijk = max(maxijk, st[x][y][len % 2]);
            }
            ans += maxijk;
        }
    }
    cout << ans;

    return 0;
}

 

posted @ 2024-09-30 14:56  五月江城  阅读(13)  评论(0编辑  收藏  举报