題目描述
現在給你N個正整數ai,每個數給出一“好數程度” gi(數值相同但位置不同的數之間可能有不同的好數程度)。對於在 i 位置的數,如果有一在j位置的數滿足 j < i 且 ai=aj,則你可以將位於[i,j]閉區間內的序列評爲“好序列”,然後獲得∑gk(j≤k≤i)(此閉區間內“好數程度”之和)分數。
注意: 在所有情況下,每個數都只能被一個”好序列”包含(只能與其他相應數被評爲”好序列”一次);在符合要求的情況下,”好序列”的評定次數不受限制,且通過不同”好序列”獲得的分數可以累加。
輸入描述:
第一行有一個正整數N。 接下來的一行有N個正整數ai,表示意義如上。 (保證ai在32位整型範圍內) 接下來的一行有N個正整數gi,表示ai的”好數程度”。 (保證gi在64位整型範圍內)
輸出描述:
一個整數,你可以獲得的最大分數(通過不同”好序列”獲得的分數可以累加),保證答案在64位整型範圍內。
示例1
輸入
7 1 2 1 2 3 2 3 1 4 3 4 3 4 5
輸出
23
備註:
數據範圍 2≤N≤300000
這道題目的解題思維很簡單,就是一個線性DP
動態轉移方程爲 如果這個位置在前面出現過
就遍歷前面這個數字出現的所有左邊位置 j dp[i] = max(dp[i] , dp[ j-1 ] + sum[i] -sum[j-1] )
如果這個位置沒有出現過 dp[i] = dp[i-1];
很普通的一個動態轉移方程, 坑點在於30w的數據, 1e的數據大小, 這個保證我們很難知道前面這個數據是否出現.
以前在數據大小比較小的時候 我們可以開一個 vector數組, 在對應數值的地方將 位置壓到vector裏面就可以了
但是這個數值範圍1e怎麼解決呢?
昨天和某位大神學長交流了一下發現了一個新操作, 可以用map去給數值進行編號, 然後vec就記錄編號的位置
因爲最多30w個數據, 所以最大也就30w個編號, 完全可以開一個vector<int> vec[30w]來存儲
那麼這樣存儲的問題就解決了,接下來放代碼
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 300005;
int n;
int sz[maxn];
ll sum[maxn];
int Nnum;//點的數量,也好給點編號
ll dp[maxn];
vector<int> vec[maxn];
map<int,int> mp;
int main(){
ll tp;
vector<int>::iterator it;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",sz+i);
sum[0] = 0,Nnum = 0;
for(int i=1;i<=n;i++){
scanf("%lld",&tp);
sum[i] = sum[i-1]+tp;
if(mp.count(sz[i])){//如果存在的話,相同編號的所有點
int tmp = mp[sz[i]];//找到這個數值的編號
for(it=vec[tmp].begin();it!=vec[tmp].end();it++)
dp[i] = max(dp[i],dp[*it-1]+sum[i]-sum[*it-1]);//vec[tmp][i]記錄的是編號爲tmp的點的位置
vec[tmp].push_back(i);
}else{
dp[i] = dp[i-1];
mp[sz[i]] = Nnum; //從零開始編號
vec[Nnum].push_back(i);//將位置壓縮進來
Nnum++;
}
}
printf("%lld\n",dp[n]);
return 0;
}