洛谷P3147题目详解(P3146加强版)

1.f[i][j]数组的意义

i表示这个区间产生的最大数,j表示这个区间的左下标,f[i][j]表示这个区间的右下标。听不懂?没关系,先来看第一个for循环。

using namespace std;
int n, ans;
int f[N][M];
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
    {
        int in;
        scanf("%d", &in);
        f[in][i] = i+1;
    }
    


    printf("%d", ans);
}

我们来看看题目给的例子吧

input                                       output
4                                                3

1 1 1 2                                    

按照它的输入我们会得到这样一组数据f[1][1]=2,f[1][2]=3,f[1][3]=4,f[2][4]=5.把它画成数轴会是怎样的呢。见下图。

 洛谷P3147题目详解(P3146加强版)_第1张图片

在这里并没有按往常一样把初始数的区间表示为[1,1],[2,2]这种,其中一个原因是我们需要一个数轴,这也和我们后面的状态转移方程有关,先看一下这个状态转移方程f[i][j] = f[i - 1][f[i - 1][j]]。同理我们把它画成一个数轴看看这是什么意思。

洛谷P3147题目详解(P3146加强版)_第2张图片

在开头我就提了f[i][j]存储的是这个区间右端的下标,这个区间是一个能够合成i这个数的区间,同理f[i-1][j]表示[ j,f[i-1][j] ]的右端下标,这个区间里面是i-1这个数,举个例子吧假设我们要求f[2][3],根据区间状态方程f[2][3]=f[1][f[1][3]],我们知道f[1][3]=4那么f[2][3]的右下标是f[1][4],但是f[1][4]并不存在,所以能够合成2的左下标为3的区间不存在。但是如果把初始区间设置为f[1][1]=1,f[1][2]=2,f[1][3]=3,f[2][4]=4会怎样,f[2][1]=f[1][f[1][1]]=f[1][1]=1.这个时候[1,1]区间的值尽然自我合成变成了2?如果一直合成下去是不是只会产生58,按照[1,1]区间这样的分法,我们完全无法区分哪个数是左下标,哪个数是又下标,所以f[in][i] = i+1;的另一个目的是区分左右下标。(其实我也不太会解释,你只要知道我们需要一段来表示一个数而不是一个点就行了)。

到这里你是不是有点混乱了。我们来理一下思路

1.i表示这个区间的数(可能是初始的,也可能是合成的),j表示这个区间的左下标,f[i][j]表示这个区间的右下标。及[i,f[i][j]]区间。

2.f[i][j] = f[i - 1][f[i - 1][j]]这个状态方程的意义是给定一个左下标和一个值i,看看是否能找到两个值为i-1的“连续”区间,那么第二个区间的值就是f[i][j]的右下标。

2.然后看一下第二个for循环

#define N 61
#define M 270007
using namespace std;
int n, ans;
int f[N][M];
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
    {
        int in;
        scanf("%d", &in);
        f[in][i] = i+1;
    }
    for (int i = 2; i <= 58; ++i)//58?????
        for (int j = 1; j <= n; ++j)
        {
            if (!f[i][j])
                f[i][j] = f[i - 1][f[i - 1][j]];
            if (f[i][j])
                ans = i;
        }
    printf("%d", ans);
}

先看数据量2<=N<=262144=2^18,每个正整数大于等于1小于等于40,两个40可以合成41,四个两个41可以合成42,两个42可以合成43这时已经消耗了2^3个40,以此类推2^18次方个58,这时最大的情况。同时i表示这个区间合成的数,同时最小的情况,两个1可以合成2,那么2<=i<=58.

j表示左下标,它小于n这很容易理解,当f[i][j]为0的时候说明这个区间要么为0要么还没有被合成过,如果不为0让ans=i,因为i只会变大,只要它不为0那就记录它,ans也会随着不为0的区间变大。只要把状态转移方程放在if里就行了,最后打印ans。

你可能感兴趣的:(算法,c++,数据结构)