P5569 [SDOI2008]石子合并(DP)

满分做法:

有一个专门解决这类问题的算法叫:GarsiaWachs算法。

算法流程:1.从序列开头往后找第一个位置\(i\)满足\(a[i]<=a[i+2]\)的点(在这里我们令\(a[n+1]\)\(a[0]\)为正无穷)。

2.将\(a[i]\)\(a[i+1]\)合并,并删除原来的两个数,后面自动补齐。

3.从\(i-1\)位置向前扫,找到第一个位置\(j\)满足\(a[j]>=a[i]+a[i+1]\),并把合并的值插入到\(j\)后面。

这样复杂度就降到了\(n²\),用vector就可以达到\(nlogn\)了。(可以用平衡树优化,但我不会┭┮﹏┭┮)。

#include<cstring>
#include<queue>
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int maxm=40007;
typedef long long ll;
int n;
vector<ll> a;
ll ans=0;
ll work()
{
  int k=a.size()-2;
  for(int i=0;i<=(int)a.size()-3;i++)//vector好像不能越界,i<=a.size()-3是错的,i<a.size()-2就对了 
  {
    if(a[i]<=a[i+2])
	{
	  k=i;
	  break;
    }
  }
  ll tmp=a[k]+a[k+1];
  a.erase(a.begin()+k);//删除K 
  a.erase(a.begin()+k);//由于数组已经向左移了一个了,因此之前的A[k+1]跑到了A[k]的位置上,所以还是删除A[k]
  int pos=-1;
  for(int i=k-1;i>=0;i--)
  {
    if(a[i]>tmp)
	{
	  pos=i;
	  break;
    }
  }
  a.insert(a.begin()+pos+1,tmp);
  return tmp;
}
int main()
{
 scanf("%d",&n);
 for(int i=1;i<=n;i++)
 {
  ll x;
  scanf("%lld",&x);
  a.push_back(x);
 }
 for(int i=1;i<=n-1;i++)
 {
   ans+=work();
 }
 printf("%lld\n",ans);
 return 0;	
}
posted @ 2019-10-21 11:13  lihan123  阅读(240)  评论(0编辑  收藏  举报