牛客網Wannafly挑戰賽21 - C 大水題

題目描述 

現在給你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;
}

 

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