题意
中文题目,不解释
题解
从DP角度来看,这道题是非常有难度的。普通的DP就只能过90%的数据,貌似正解需要用到四边形优化。。。不过呢,有一位叫GarsiaWachs的大牛提出了一套算法,可以在O(N^2)的复杂度下解决这个问题。(原本的DP复杂度为O(N^3),据说使用平衡树优化以后可以达到O(NlogN),不过对于这道题没什么必要了)
网上关于GarsiaWachs算法有很多讲解,这里也简单讲一下。大致流程就是从序列中找出d[i]<=d[i+2]的那个位置,然后删除i和i+1位置上的元素。并且把他们的和放置到从i向前遍历第一个大于两数之和的元素之后。(网上有很多在这里没有讲清楚,这里重新阐述一下!)另外需要注意,如果遍历完前面所有元素都没有符合条件的元素,那么我们就将它们的和放置到链表的顶端。另外还需要注意,我们需要向链表最后加入一个值为INF的元素,以便于最后的合并处理(因为最后的合并处理也最少需要三个元素,用来保证d[i]<=d[i+2])
代码
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include<string.h>
#include<list>
#include<iostream>
#define LL long long
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(t) while(t)
#define MEM(a,b) memset(a,b,sizeof(a))
#define COUT(x) cout<<x<<endl
using namespace std;
long INF = 1e12;
list<int> a;
int main() {
int n;
W(~scanf("%d",&n)) {
if(n==0)
break;
a.clear();
for (int i = 0; i < n; i++) {
int now;
scanf("%d",&now);
a.push_back(now);
}
a.push_back(INF);
int now=n;
int ans=0;
for (int i = 0; i < n-1; i++) {
list<int>::iterator it=a.begin();
int m=0;
list<int>::iterator stop;
while(it!=a.end()) {
list<int>::iterator aa=it;
aa++,aa++;
list<int>::iterator after=it;
after++;
if(*it<*aa) {
stop=aa;
m=*it+*after;
ans+=m;
a.erase(it);
a.erase(after);
break;
}
it++;
}
it=stop;
list<int>::iterator mx;
bool has=false;
if(stop!=a.begin()){
it--;
while(true) {
if(*it==INF){
if(it==a.begin()) break;
it--;
continue;
}
if(*it>m) {
it++;
a.insert(it,m);
has=true;
break;
}
if(it==a.begin()) break;
it--;
}
}
if(!has) {
a.push_front(m);
}
}
printf("%d\n",ans);
}
}