P5569 SDOI2008 石子合并(黑科技)
Posted liuz8848
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P5569 SDOI2008 石子合并(黑科技)相关的知识,希望对你有一定的参考价值。
(Solution)
当(n)在(100)左右时,直接(O(n^3))区间(DP)
当(n)在(40000)左右时,需要用贪心算法:加西亚-瓦克斯算法((Garsia Wachs))
注:这个方法仅求石子合并的最小答案
这是大概的流程
这是关于(Garsia Wachs)算法的正确性证明:传送门
时间复杂度最坏为(O(n^2)),但是基本跑不满,数据随机的话(n=40000)能搞过去
代码使用链表实现,方便删除和插入操作,边界什么的比较麻烦,一定要注意,可以看看代码注释
(Code)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define re register
#define maxn 1000010
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
ll ans,tmp;
int l[maxn],r[maxn],v[maxn],n,a[maxn],cnt,num;
int main()
{
n=read();
for(re int i=1;i<=n;++i)
{
a[i]=read();
v[i]=a[i],l[i]=i-1,r[i]=i+1;
}
v[0]=v[n+1]=INF;//赋为无穷大作为边界,保证所有删除插入都在边界内完成
r[0]=1,l[0]=-1;//这里要赋成-1,否则到了0,l[0]=0会一直死循环
l[n+1]=n,r[n+1]=0;
cnt=n;
num=n+1;//从n+2开始编号
while(cnt>1)//=1的时候跳出就行了
{
cnt--;
int now=0;
while(r[r[now]]!=0)//不要写>=n+1,因为新的编号是>=n+1的,往右找是0就到了真是的边界,退出
{
if(v[r[r[now]]]>=v[now]) break;
now=r[now];
}
tmp=v[now]+v[r[now]];
ans+=tmp;统计答案
r[l[now]]=r[r[now]];
l[r[r[now]]]=l[now];//删除操作,删除两个
now=l[now];
while(now>=0)//这里可以写>=0,往左是-1就代表到边界了
{
if(v[now]>tmp) break;
now=l[now];
}
num++;
l[num]=now,r[num]=r[now],v[num]=tmp;
r[now]=num;//插入操作
l[r[num]]=num;
}
printf("%d
",ans);
return 0;
}
以上是关于P5569 SDOI2008 石子合并(黑科技)的主要内容,如果未能解决你的问题,请参考以下文章