bzoj 4709 [Jsoi2011]柠檬

本文探讨了一种结合动态规划与单调栈思想的算法优化方案,重点在于如何通过维护一个单调栈来避免冗余计算,确保状态转移的有效性和效率。

http://www.elijahqi.win/archives/3792
动态规划+单调栈的思想

斜率优化那个i实在没看懂

  先附上neither_nor的题解.

考虑DP,f[i]表示前i个的最大价值.

那么在最后一段所选的颜色一定是i的情况下,最后一段的开头的颜色也一定是i的颜色,否则开头一段也没有贡献,不然单分出去一段.

我们发现对于任意一个i,他所选的最后一段所指定的颜色一定是i的颜色,因为否则的话i这个点就会没有贡献,一定不如最后一段只选一个i.

那么对于f[i],假设之前存在一个点j,这个点的颜色a[j]与i的颜色a[i]相等,那么我们可以让f[i]由j转移,价值为f[j-1]+a[j]*(s[i]-s[j]+1)^2,s[i]代表前i个数里a[i]出现的次数

我们发现对于任意一个颜色x,随着这个颜色的数不断出现,由每一个之前的a[j]=x的j转移所产生的价值都会不断增加,且j越小,增加的越块,如果有j1< j2,并且j1当前的价值比j2大了,那么j2就没有用了,于是我们可以维护一个单调栈,每次如果栈顶第二个的元素超过了栈顶,就把栈顶弹出,决策的时候直接用栈顶决策

而我们发现可能会出现栈顶第三个元素超过了栈顶,而栈顶第二个元素还没超过栈顶的情况,我们需要避免这种情况。我们发现对于任意的j1< j2< i1< i2,如果j1超过i1的时间比j2超过i1的时间要早,那么j1超过i2的时间也一定比j2超过i2的时间早,证明显然,并且我们对于任意的(j1,j2),j1< j2,我们都能直接算出在第几个颜色与j1,j2相等的数出现的时候,j1会超过j2,那么在我们即将把i压入栈顶的时候,只要当前栈顶第二个元素超过i的时间比当前栈顶超过i的时间要早,那么栈顶就也没用了,我们就可以弹栈,这样的话栈里的每一个元素超过上一个元素的时间也是单调的,于是就没有问题了.

注意到 我们需要避免每个元素超过上一个的时间不是单调

设分别为j1,j2如果 j2超过j1相比j1超过i要早说明j1一定弹掉

  另一位大爷对最后一段的理解是:

  因为我们要避免每一个元素超过上一个元素的时间也是单调的, 那么设栈里第二个元素为j2, 栈顶即第一个元素为j1, 即将加入栈的为i, 那么当j2超过j1的时候比j1超过i的时候早,那么我们弹栈. 先考虑为什么这么做, 首先这样能弹栈说明了j1一定不优. 证明: 由于本身i这个地方可以自成一段,也就是说i是可以用i点这个决策的, 那么当j1比i优的时候, j1不应该弹栈,否则肯定弹栈.

但又因为j1如果比i优的话, 当j1比i优的时候, j2已经比j1优了(这是弹栈条件), 所以j1还是要被弹栈. 所以满足这个条件弹栈肯定是没有问题的. 接下来我们再看为什么这样就能保证每一个元素超过上一个元素的时间也是单调的,因为我们通过弹栈保证了, j2比j1优的时间一定 比 j1比i优的时间晚(否则j1就被弹出去了), 那么因为我们一直这样保证, 所以当i已经不再是栈顶元素是, 因为j2比j1优的时间一定比j1比i优的时间晚, 那么设压在i之上的元素是p, 我们也保证了j1比i优的时间晚肯定比 i比p优的时间晚(否则我们要弹栈, 现在讨论的是一个合法的即真正单调的单调栈). 这样递推下去,栈中一层比一层晚, 所以就不会出现栈顶第三个元素超过了栈顶,而栈顶第二个元素还没超过栈顶的情况.

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
const int N=100005;
vector<int> q[N>>3];
int a[N],c[N>>3],s[N],n;ll dp[N];
inline ll gao(int x,int v){
    return dp[x-1]+(ll)a[x]*v*v;
}
inline int calc(int x,int y){
    int l=1,r=n,res=n+1;
    while(l<=r){
        int mid=l+r>>1;
        if (gao(x,mid-s[x]+1)>=gao(y,mid-s[y]+1)) res=mid,r=mid-1;else l=mid+1;
    }return res;
}
int main(){
    freopen("bzoj4709.in","r",stdin);
    n=read();
    for (int i=1;i<=n;++i){
        //printf("%d\n",i);
        int x=read(),size;a[i]=x;++c[x];s[i]=c[x];size=q[x].size();
        while(size>=2&&calc(q[x][size-2],q[x][size-1])<=calc(q[x][size-1],i)) q[x].pop_back(),size=q[x].size();
        q[x].push_back(i);size=q[x].size();
        while(size>=2&&calc(q[x][size-2],q[x][size-1])<=s[i]) q[x].pop_back(),size=q[x].size();
        int j=q[x].back();dp[i]=gao(j,s[i]-s[j]+1);
    }printf("%lld\n",dp[n]);
    return 0;
}
【Koopman】遍历论、动态模态分解和库普曼算子谱特性的计算研究(Matlab代码实现)内容概要:本文围绕【Koopman】遍历论、动态模态分解和库普曼算子谱特性的计算研究展开,重点介绍基于Matlab的代码实现方法。文章系统阐述了遍历理论的基本概念、动态模态分解(DMD)的数学原理及其与库普曼算子谱特性之间的内在联系,展示了如何通过数值计算手段分析非线性动力系统的演化行为。文中提供了完整的Matlab代码示例,涵盖数据驱动的模态分解、谱分析及可视化过程,帮助读者理解并复现相关算法。同时,文档还列举了多个相关的科研方向和技术应用场景,体现出该方法在复杂系统建模与分析中的广泛适用性。; 适合人群:具备一定动力系统、线性代数与数值分析基础,熟悉Matlab编程,从事控制理论、流体力学、信号处理或数据驱动建模等领域研究的研究生、博士生及科研人员。; 使用场景及目标:①深入理解库普曼算子理论及其在非线性系统分析中的应用;②掌握动态模态分解(DMD)算法的实现与优化;③应用于流体动力学、气候建模、生物系统、电力系统等领域的时空模态提取与预测;④支撑高水平论文复现与科研项目开发。; 阅读建议:建议读者结合Matlab代码逐段调试运行,对照理论推导加深理解;推荐参考文中提及的相关研究方向拓展应用场景;鼓励在实际数据上验证算法性能,并尝试改进与扩展算法功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值