标签:时间复杂度 时间 break def 需要 har efi 插入 贪心算法
当\(n\)在\(100\)左右时,直接\(O(n^3)\)区间\(DP\)
当\(n\)在\(40000\)左右时,需要用贪心算法:加西亚-瓦克斯算法(\(Garsia\ Wachs\))
注:这个方法仅求石子合并的最小答案
这是大概的流程
这是关于\(Garsia\ Wachs\)算法的正确性证明:传送门
时间复杂度最坏为\(O(n^2)\),但是基本跑不满,数据随机的话\(n=40000\)能搞过去
代码使用链表实现,方便删除和插入操作,边界什么的比较麻烦,一定要注意,可以看看代码注释
#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\n",ans);
return 0;
}
标签:时间复杂度 时间 break def 需要 har efi 插入 贪心算法
原文地址:https://www.cnblogs.com/Liuz8848/p/11723914.html