P5569 [SDOI2008] 石子合并 解题报告

P5569 [SDOI2008] 石子合并解题报告:

题意

(n) 个石子排成一排,第 (i) 堆有 (a_i) 个,每次可以选取两个石子堆合并,得分是新石子堆的大小,求合并所有石子为一堆的最小得分。

(nleqslant 40000)

分析

大众科技,GarsiaWachs 算法。

设当前石子共 (w) 堆,分别为 (v_{1cdots w})

  1. 找到最小的 (k) 满足 (v_{k-1}<v_{k+1}),然后找到最大的 (p) 满足 (p<k)(v_p>v_k+v_{k-1})
  2. 合并第 (k,k+1) 堆石子,并将其放在第 (p) 堆石子后面;
  3. 重复操作直到 (w=1)

证明鸽了。

注意要在序列两端放置两个哨兵点。

复杂度:(O(n^2)),开 O2 能过,可以用平衡树优化到 (O(nlog n))

实际上有一个扩展,但是我还不会。

代码

#include<stdio.h>
#include<vector>
#define inf 2000000000
using namespace std;
const int maxn=40005;
int n,ans;
int a[maxn];
vector<int>v;
int main(){
	scanf("%d",&n);
	v.push_back(inf);
	for(int i=1,x;i<=n;i++)
		scanf("%d",&x),v.push_back(x);
	v.push_back(inf);
	for(int i=1;i<n;i++){
		int k=0,p=0,rec=0;
		for(int j=1;j<=n;j++)
			if(v[j-1]<v[j+1]){
				k=j;
				break;
			}
		rec=v[k]+v[k-1],ans+=rec;
		for(int j=k-1;j>=0;j--)
			if(v[j]>rec){
				p=j;
				break;
			}
		v.erase(v.begin()+k),v.erase(v.begin()+(k-1)),v.insert(v.begin()+p+1,rec);
	}
	printf("%d
",ans); 
	return 0;
}
原文地址:https://www.cnblogs.com/xiaoziyao/p/15316494.html