Hackerrank Subsequence Weighting(单调set 数状数组)

A subsequence of a sequence is a sequence which is obtained by deleting zero or more elements from the sequence. 

You are given a sequence A in which every element is a pair of integers  i.e  A = [(a1, w1), (a2, w2),..., (aN, wN)].

For a subseqence B = [(b1, v1), (b2, v2), ...., (bM, vM)] of the given sequence : 

  • We call it increasing if for every i (1 <= i < M ) , bi < bi+1.
  • Weight(B) = v1 + v2 + ... + vM.

Task: 
Given a sequence, output the maximum weight formed by an increasing subsequence.

Input: 
The first line of input contains a single integer TT test-cases follow. The first line of each test-case contains an integer N. The next line contains a1, a2 ,... , aN separated by a single space. The next line contains w1, w2, ..., wN separated by a single space.

Output: 
For each test-case output a single integer: The maximum weight of increasing subsequences of the given sequence.

Constraints: 
1 <= T <= 5 
1 <= N <= 150000 
1 <= ai <= 109, where i ∈ [1..N] 
1 <= wi <= 109, where i ∈ [1..N]

Sample Input:

2  
4  
1 2 3 4  
10 20 30 40  
8  
1 2 3 4 1 2 3 4  
10 20 30 40 15 15 15 50

Sample Output:

100  
110

Explanation: 
In the first sequence, the maximum size increasing subsequence is 4, and there's only one of them. We choose B = [(1, 10), (2, 20), (3, 30), (4, 40)], and we have Weight(B) = 100.

In the second sequence, the maximum size increasing subsequence is still 4, but there are now 5 possible subsequences:

1 2 3 4  
10 20 30 40

1 2 3 4  
10 20 30 50

1 2 3 4  
10 20 15 50

1 2 3 4  
10 15 15 50

1 2 3 4  
15 15 15 50

Of those, the one with the greatest weight is B = [(1, 10), (2, 20), (3, 30), (4, 50)], with Weight(B) = 110.

Please note that this is not the maximum weight generated from picking the highest value element of each index. That value, 115, comes from [(1, 15), (2, 20), (3, 30), (4, 50)], which is not a valid subsequence because it cannot be created by only deleting elements in the original sequence.

题意:给定两个序列和 要求最大的第二个序列和 满足第一个序列满足递增。

解法:这题的背景应该是经典的最大上升子序列和。但考虑到那是一个O(n^2)算法。需要在这个的基础上进行优化。

解法1:维护一个单调set

有一件事可以确定,我们要是得大的数字的权值和尽可能大。这也是单调的意义所在。

如果一个数大但权值和小,我们可以不考虑这个数,因为有一个比它小的数可以添加构成更大的序列。

首先离散化,将所有的数字排序并离散化成由1开始递增的数列(用map)

用val[i]表示这个数字能构成的最大值。

然后每次插入时向后维护,移除那些不符合单调性的元素。

查找的时候进行二分。

但这样其实很慢,要1秒多。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define pii pair
#define vi vector
#define ll long long
#define eps 1e-3
using namespace std;
const int maxn = 2e5 + 10;
map ind;
ll a[maxn];
ll w[maxn];
set temp;
ll val[maxn];
int main()
{
    //freopen("/Users/vector/Desktop/in", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T;
    cin >> T;
    while(T--)
    {
        int n;
        cin >> n;
        temp.clear();
        memset(val, 0, sizeof(val));
        ind.clear();
        for(int i = 0; i < n; i++)
        {
            cin >> a[i];
            temp.insert(a[i]);
        }
        for(int i = 0; i < n; i++)
            cin >> w[i];
        int id = 1;
        for(auto idx: temp)
            ind[idx] = id++;
        temp.clear();
        for(int i = 0; i < n; i++)
        {
            int q = ind[a[i]];
            /*for(auto idx = temp.rbegin(); idx != temp.rend(); idx++)
            {
                int elm = *idx;
                //cout << elm << endl;
                if(q > elm)
                {
                    temp.insert(q);
                    val[q] = max(val[q], val[elm] + w[i]);
                    f = 1;
                    break;
                }
            }*/
            set :: iterator io = temp.lower_bound(q);
            if(io == temp.begin())
            {
                temp.insert(q);
                val[q] = max(val[q], w[i]);
            }
            else
            {
                io--;
                int elm = *io;
                temp.insert(q);
                val[q] = max(val[q], val[elm] + w[i]);
            }
            set :: iterator t = temp.find(q);
            vector del;
            for(; t != temp.end(); t++)
            {
                if(val[*t] < val[q])
                    del.push_back(*t);
            }
            for(auto idx: del)
                temp.erase(idx);
            //cout << endl;
        }
//        for(auto t: temp)
//            cout << t << ' ';
        int ed = *temp.rbegin();
        cout << val[ed] << endl;


    }
    return 0;
}

 

解法2:数状数组(bit)

用数状数组维护一段区间的最大值(记住了:数状数组也可以用于维护区间最大值)

读入,去重,每个数字从小到大对应为下标0、1、2.....

然后每次处理一个数,用lower_bound寻找大于等于它的第一个数,由于树状数组的下标从1开始,所以这里正好找的是前一个数

查询1-当前下标的最大值+w[i]

再更新即可。还是由于下标,要更新的是下一位。很巧妙了。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define pii pair
#define vi vector
#define ll long long
#define eps 1e-3
using namespace std;
const int maxn = 2e2 + 10;
map ind;
int w[maxn];
int val[maxn];
ll bit[maxn];
int n;
void update(int x, ll v)
{
    while(x < maxn)
    {
        bit[x] = max(bit[x], v);
        x += x & -x;
    }
}
ll query(int x)
{
    ll res = 0;
    while(x)
    {
        res = max(res, bit[x]);
        x -= x & -x;
    }
    return res;
}
int main()
{
    //freopen("/Users/vector/Desktop/in", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T;
    cin >> T;
    while(T--)
    {
        cin >> n;
        vi a(n), b(n);
        for(int i = 0; i < n; i++)
            cin >> a[i];
        b = a;
        sort(b.begin(), b.end());
        b.erase(unique(b.begin(), b.end()), b.end());//将元素去重
//        for(auto idx: b)
//            cout << idx  << ' ' ;
        for(int i = 0; i < n; i++)
            cin >> w[i];
        ll ans = 0;
        memset(bit, 0, sizeof(bit));
        //bit的下标是从1开始的 正好对应的前一位
        for(int i = 0; i < n; i++)
        {
            int pos = (int)(lower_bound(b.begin(), b.end(), a[i]) - b.begin());
            ll temp = query(pos) + w[i];
            ans = max(temp, ans);
            update(pos + 1, temp);//更新当前位置的值 要记住bit的下标是从1开始的
        }
        cout << ans << endl;
        
    }
    return 0;
}
/*
2
4
1 2 3 4
10 20 30 40
8
1 2 3 4 1 2 3 4
10 20 30 40 15 15 15 50
*/

 

你可能感兴趣的:(线段树&数状数组,DP)