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 <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <iomanip>
#include <cmath>
#include <climits>
#include <set>
#include <map>
#include <queue>
#define pii pair<int, int>
#define vi vector<int>
#define ll long long
#define eps 1e-3
using namespace std;
const int maxn = 2e5 + 10;
map<ll, ll> ind;
ll a[maxn];
ll w[maxn];
set<int> 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<int> :: 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<int> :: iterator t = temp.find(q);
            vector<int> 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 <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <iomanip>
#include <cmath>
#include <climits>
#include <set>
#include <map>
#include <queue>
#define pii pair<int, int>
#define vi vector<int>
#define ll long long
#define eps 1e-3
using namespace std;
const int maxn = 2e2 + 10;
map<int, int> 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
*/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章