1. 程式人生 > >[動態規劃]ACM預選賽F題 俠客行

[動態規劃]ACM預選賽F題 俠客行

這道題是我覺的這次預選賽中題目最難的一道,比賽過程中我並沒有做出來。
這種動態規劃的思路太巧秒了,我是想不到的。
再一次膜拜jxy學長

題目連結

描述

眾所周知,龍島主和木島主發現了俠客島上的武功祕訣,這武功祕訣是李白《俠客行》的圖解,含義極是深奧繁複。他二人修習數月後,忽對圖解中所示武功產生了歧見。二人相互辯難剖析,鑽研其中道理,然終不能解。於是廣邀天下奇才異能之士同來島上,各竭心思,一起參研。恰好其時島上的“斷腸蝕骨腐心草”開花,此草若再配以其他佐使之藥,熬成熱粥,服後於練武之士大有裨益。於是派出使者,邀請當世名門大派的掌門人、各教教主、各幫幫主到俠客島喝碗臘八粥,再請他們去參研圖解。現有許多人到了俠客島,他們決定先進行一番較量,當然了,點到為止。

現有n個人排成一行,編號為1…n,每個人的內力為ai。

對於每兩個人(這兩個人可以是同一個人),這兩個人及其之間的所有人進行一次較量,勝者為他們中內力最大者。求所有比賽中勝者內力和。

如果這場比賽只有一個人的話,他本身就是獲勝者。

輸入

第一行包含一個整數 T (0<T<=1000)表示有 T 組測試資料

對於每組資料輸入格式如下:

第一行為一個整數n (1<n<=100000)表示人的數量。

第二行為n個整數ai,(0<ai<10^9),表示第i個人的內力。

資料保證n的總和不超過10^6。(資料加強了,請用 long long)

輸出

輸出一個整數,為所有比賽中勝者內力和

輸入樣例

2
4
1 2 3 4
5
2 3 1 4 2

輸出樣例

30
49

提示

Sample1

(max(1)+max(2)+max(3)+max(4)) + (max(1,2)+max(2,3)+max(3,4)) + (max(1,2,3)+max(2,3,4)) +(max(1,2,3,4))

=(1+2+3+4) + (2+3+4) + (3+4) +4

Sample2

(2+3+1+4+2) + (3+3+4+4) + (3+4+4) + (4+4) + (4)


題解

這道題目讓我們求出在所有任意區間中最大值的和
題目資料量很大隻能用 O(n)左右的演算法
一下程式碼是學長的,我複寫了一遍 加了幾行註釋 希望可以幫忙理解

//膜拜出題人大佬 
#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;

const LL maxa = 1e11;
const int maxn = 1e6 + 10;
LL a[maxn];
LL sum[maxn];
int p[maxn];//指向第i個人從左依次數第一個比他能力強的人

int main(){
	int t;
	cin >> t;
	while(t--){
		LL ans = 0;
		int n;
		scanf("%d", &n);
		scanf("%d", &a[1]);
		sum[0] = p[1] = p[0] = 0;
		a[0] = maxa, ans = sum[1] = a[1];
		for(int i = 2; i <= n; i++){
			scanf("%d", &a[i]);
			p[i] = i - 1;
			while(a[p[i]] <= a[i])
				p[i] = p[p[i]];
			sum[i] = sum[p[i]] + a[i]*(i - p[i]);
			//這第i個人從左依次數第一個比他能力強的人 在他左邊區間勝的場數和帶上第i個人的區間勝的場數一樣多 
			//所以只用p[i]指向左邊比他大的人,真的妙 
			ans += sum[i];
		}
		cout << ans << "\n";
	}
	return 0;
}