P7421 子序列 题解
官方题解。
upd 2021.3.7:将 p 的下标改成括号形式,避免了下标不清。
这题赛时 AC 人数甚至没有压轴鱼神的多项式题人多?
把式子的前几项和后几项写出来:
很难贪心,考虑 dp。发现状态中只记单独一个位置不好 dp,式子中
设计状态
当然可以不转移,将
将
复杂度瓶颈在求
将转移方程中的括号拆开:
将只与
枚举
然后发现,与
这个东西中包含一个
- 枚举
x ,通过g 快速计算f_{i,j} 的值; - 将
(i,j) 当做g 中的(x,y) ,枚举g 中的i ,更新g 。
这段还是看程序比较好懂吧,可以先跳到下面程序部分搞懂再往下。
然后,还有一点细节要注意:
还有一个小问题:
时间复杂度
#include<bits/stdc++.h>
using namespace std;
long long a[305],f[305][305],g[305][305];
inline int read()
{
char c=getchar();
int x=0,y=1;
while(c<'0'||c>'9')
{
if(c=='-')
y=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
}
return x*y;
}
int main()
{
int n=read(),i,j,k;
long long ans=-1000000000000000000ll;
for(i=1;i<=n;i++)
a[i]=read();
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
g[i][j]=-1000000000000000000ll;
for(j=n;j;j--)//外层枚举j并倒序枚举,上面有解释
for(i=j-1;i;i--)//内层枚举i并倒序枚举,上面有解释
{
f[i][j]=a[i]*(n-j+1)-a[j]*i;//不转移的情况
for(k=1;k<i;k++)//这里的k就是转移方程中的x
f[i][j]=max(f[i][j],g[i][k]-a[i]*j+a[k]*j-a[j]*i+a[j]*k);
ans=max(ans,f[i][j]+(a[j]-a[i])*(j-i));
for(k=i+1;k<j;k++)//这里的k就是g[i][x]中的i
g[k][i]=max(g[k][i],f[i][j]+a[k]*j-a[i]*j+a[j]*k-a[j]*i);
}
for(i=1;i<=n;i++)
ans=max(ans,a[i]*(n-i+1)-a[i]*i);
for(i=1;i<n;i++)
for(j=i+1;j<=n;j++)
for(k=i+1;k<j;k++)
ans=max(ans,f[i][j]+(a[k]-a[i])*(j-k)+(a[j]-a[k])*(k-i));
printf("%lld",ans);
return 0;
}