Examples

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

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

题意

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

\(n\leqslant 40000\)

分析

大众科技,GarsiaWachs 算法。

设当前石子共 \(w\) 堆,分别为 \(v_{1\cdots 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(n\log 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\n",ans); 
	return 0;
}
posted @ 2021-09-21 13:49  xiaoziyao  阅读(72)  评论(0编辑  收藏  举报