HDU 6570 Wave(波浪)(基本状态转移)

本文探讨了如何在给定序列中寻找最长的波浪子数组,即满足特定条件的子序列,包括奇数位与偶数位元素分别相同但不相等。文章提供了两种解题思路,一种是优化后的暴力解法,另一种是使用状态转移方程的正规解法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem Description

Avin is studying series. A series is called "wave" if the following conditions are satisfied:
1) It contains at least two elements;
2) All elements at odd positions are the same;
3) All elements at even positions are the same;
4) Elements at odd positions are NOT the same as the elements at even positions.
You are given a series with length n. Avin asks you to find the longest "wave" subseries. A subseries is a subsequence of a series.

埃尔文在学习序列。一个“波浪数组”(摆动数组)要满足以下条件:

1 包含至少两个元素(最多也是)

2 所有奇数位元素相同

3 所有偶数位元素相同

4 奇数位和偶数位元素不同

给定一个n长度的序列,埃尔文要求你去寻找最长的波浪子数组,一个子数组是序列的子序列。

Input

The first line contains two numbers n, c (1 ≤ n ≤ 100, 000, 1 ≤ c ≤ 100). The second line contains n integers whose range is [1, c], which represents the series. It is guaranteed that there is always a "wave" subseries.

Output

Print the length of the longest "wave" subseries.

Sample Input

5 3 1 2 1 3 2

Sample Output

4

由于本题关键变量c的数据范围较小,所以为此题提供了一种暴力解题的可能性。换句话说,只需要从序列中找到两个数字,使得它们构成的波浪数组最长即可。

表面上看复杂度是n*c*c(位置与枚举),达到了10的九次方,但是在一次仅有两个数字需要关心,所以直接记录这两个数字的位置,跳过其他数字即可。那么就可把n优化为n/c(需遍历长度),将复杂度降为nc,10的七次方,可在1秒内结束运行。

在遍历n/c长度的数组时,我们还得确定一个先后的问题,也就是得记录一下前一个数字最后出现的位置。

所以思路如下:

  1. 利用vector或者其他东西,存储各个数字在序列中出现的位置
  2. 利用枚举循环,在这些数字存在的位置中遍历即可。

 

//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf_s("%lld", &a)
#define println(a) printf("%lld\n", a)
#define reset(a, b) memset(a, b, sizeof(a))
const int INF = 0x3f3f3f3f;
using namespace std;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1000000007;
const int tool_const = 19991126;
const int tool_const2 = 33;
inline ll lldcin()
{
    ll tmp = 0, si = 1;
    char c;
    c = getchar();
    while (c > '9' || c < '0')
    {
        if (c == '-')
            si = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        tmp = tmp * 10 + c - '0';
        c = getchar();
    }
    return si * tmp;
}
///Untersee Boot IXD2(1942)
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
/**Last Remote**/
vector<ll>vec[500];
int DETERMINATION()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    ll n, c;
    cin >> n >> c;
    ll tmp;
    for (int i = 1; i <= n; i++)
    {
        cin >> tmp;
        vec[tmp].push_back(i);
    }//这里是记录各个数字出现的位置
    ll ans = 0;
    for (int i = 1; i <= c; i++)
    {
        for (int j = 1; j <= c; j++)//枚举范围内数字
        {
            if (i == j)
                continue;
            ll iloc = 0, jloc = 0, previous = 0, tmpans = 0;
            ll ilimit = vec[i].size(), jlimit = vec[j].size();
            //cout << i << " " << j << endl;
            while (1)//进行遍历
            {
                while (iloc < ilimit&&vec[i][iloc] < previous)//在先前位置之后,找到一个目标数字的合法位置
                    iloc++;
                if (iloc == ilimit)
                    break;
                else
                {
                    previous = vec[i][iloc];//更新先前位置
                    tmpans++;
                }
                while (jloc < jlimit&&vec[j][jloc] < previous)
                    jloc++;
                if (jloc == jlimit)
                    break;
                else
                {
                    previous = vec[j][jloc];
                    tmpans++;
                }
            }
            ans = max(tmpans, ans);
        }
    }
    cout << ans << endl;
    return 0;
}

当然,暴力解题只是侥幸。正规的做法是对两个c范围内的数字所能组成的波浪数组的最大值列状态转移方程。啥意思?就是对整个序列出现的数字按照次序全部记录下来,然后从原序列中逐一遍历数字,通过状态转移方程找到这两个数字可组成的波浪数组的长度的最大值。

这里的状态转移方程只是代表了某两个数字可组成多长的波浪数组,因此用二维数组即可。

所以思路如下:

  1. 存储每个数字的出现次序
  2. 以原序列和存储数组作两层循环,利用状态转移方程找到最大值。 
//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
#define DETERMINATION main
#define lldin(a) scanf_s("%lld", &a)
#define println(a) printf("%lld\n", a)
#define reset(a, b) memset(a, b, sizeof(a))
const int INF = 0x3f3f3f3f;
using namespace std;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1000000007;
const int tool_const = 19991126;
const int tool_const2 = 33;
inline ll lldcin()
{
    ll tmp = 0, si = 1;
    char c;
    c = getchar();
    while (c > '9' || c < '0')
    {
        if (c == '-')
            si = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        tmp = tmp * 10 + c - '0';
        c = getchar();
    }
    return si * tmp;
}
///Untersee Boot IXD2(1942)
/**Although there will be many obstructs ahead,
the desire for victory still fills you with determination..**/
/**Last Remote**/
ll dp[250][250], secq[530000], vis[5000];
ll a[650000];
int DETERMINATION()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    ll n, c;
    cin >> n >> c;
    ll cnt = 0;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        if (vis[a[i]] == false)
        {
            secq[++cnt] = a[i];//存储它的出现次序
            vis[a[i]] = true;//已经出现在存储数组里了,就没必要再存储了
            dp[0][a[i]] = 1;//至少就他自己而言,可以组成1个长度的波浪数组,不符题意但是存在
        }
    }
    ll ans = 0;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= cnt; j++)
        {
            if (a[i] != secq[j])//这两个数不能一样
            {
                ll tmp = max(dp[0][secq[j]] + 1, dp[a[i]][secq[j]] + 1);//看这个数是新创建了一个波浪数组还是在原有的波浪数组上延长
                dp[secq[j]][a[i]] = max(tmp, dp[secq[j]][a[i]]);
        //反过来也一样
                ans = max(dp[secq[j]][a[i]], ans);
            }
        }
    }
    cout << ans-1 << endl;
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值