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 石子合并(黑科技)的主要内容,如果未能解决你的问题,请参考以下文章

2298 石子合并 2008年省队选拔赛山东

[08山东省选]2298 石子合并

[黑科技]树上启发式合并 初步

[SDOI2008]洞穴勘测

[SDOI2009]E&D

[SDOI2009]E&D