动态规划的优化

动态规划的优化

方法一:分离变量后使用单调队列

P3648.序列分割

正常dp做法

首先,可以证明切割次序与分数无关。

所以我们用\(f[i][k]\)表示区间前i个数切k次得到的最大分数值,并状态转移

\[f[i][k]=max\{f[i][k],f[j][k-1]+s[j]*(s[i]-s[j])\} \]

代码:

// Problem: P3648 [APIO2014] 序列分割
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3648
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define int long long
using namespace std;
const int mn=100005,mk=205;
int n,k,f[mn][mk],a[mn],sum[mn],nxt[mn][mk];
void out(int x,int y)
{
	if(x==0)return;
	if(y==1)return (void)printf("%lld ",x);
	out(nxt[x][y-1],y-1);
	printf("%lld ",x);
}
signed main()
{
	memset(f,-0x3f,sizeof(f));
	scanf("%lld%lld",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		sum[i]=sum[i-1]+a[i];
		f[i][0]=0;
	}
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<min(i,k+1);j++)
		{
			for(int l=1;l<i;l++)
			{
				if(f[i][j]<=f[l][j-1]+sum[l]*(sum[i]-sum[l]))
				{
					f[i][j]=f[l][j-1]+sum[l]*(sum[i]-sum[l]);
					nxt[i][j]=l;
				}
			}
		}
	}
	printf("%lld\n",f[n][k]);
	out(nxt[n][k],k);
	return 0;
}

复杂度\(O(n^{2})\),拿到50pts.

空间优化

可以改变循环顺序缩维。

注意到\(f[i][j]\)更新时只会用到\(f[][j-1]\)里的内容

考虑先遍历第二维,这样可以缩掉第二维

// Problem: P3648 [APIO2014] 序列分割
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3648
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define int long long
using namespace std;
const int mn=100005,mk=205;
int n,k,f[mn],a[mn],sum[mn],nxt[mn][mk];
void out(int x,int y)
{
	if(x==0)return;
	if(y==1)return (void)printf("%lld ",x);
	out(nxt[x][y-1],y-1);
	printf("%lld ",x);
}
signed main()
{
	scanf("%lld%lld",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	for(int j=1;j<=k;j++)
	{
		for(int i=n;i>=j+1;i--)
		{
			for(int l=1;l<i;l++)
			{
				if(f[i]<=f[l]+sum[l]*(sum[i]-sum[l]))
				{
					f[i]=f[l]+sum[l]*(sum[i]-sum[l]);
					nxt[i][j]=l;
				}
			}
		}
	}
	printf("%lld\n",f[n]);
	out(nxt[n][k],k);
	return 0;
}

优化

考虑方程现在为

\[ \]

\(i\)\(j\)优于\(l\)\(j<l\)

\[f[j]+s[j]*s[i]-s[j]^2>f[l]+s[l]*s[i]-s[l]^2 \]

\[f[j]-f[l]-s[j]^2+s[l]^2>(s[l]-s[j])*s[i] \]

\[\frac {f[j]-s[j]^2-(f[l]-s[l]^2)}{-s[l]-(-s[j])}>s[i] \]

设左式为\(g(j,l)\)右式为\(h(i)\)

\[j比l更优\leftrightarrow g(j,l)>h(i) \]

\[j和l一样优\leftrightarrow g(j,l)==h(i) \]

\[j比l更劣\leftrightarrow g(j,l)<h(i) \]

本题中,

\[f[m][i]=\displaystyle{max}_{0\le j <i}\{f[m-1][j]+s[j]*(s[i]-s[j])\}; \]

固定m,对于\(j_1<j_2\)\(j_2\)优于\(j_1\)意味着\(g(j_1,j_2)<h(i)\),又已知\(h(i)\)单调递增,所以在某个m后\(j_2\)一直优于\(j_1\),而在此之前则相反。

我们考虑用单调队列维护现存可能合法的\(j\)

如果存在三个决策\(j_1<j_2<j_3\),且\(g(j_1,j_2)>g(j_2,j_3)\)则可以直接踢掉\(j_2\)

如果队首两元素的函数值小于当前h(i),可以踢掉第一个元素。

// Problem: P3648 [APIO2014] 序列分割
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3648
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define int long long
#define inf 0x3f3f3f3f
using namespace std;
const int mn=100005,mk=205;
int n,k,f[mn],a[mn],sum[mn],nxt[mn][mk],g[mn];
int q[mn],hd,bk;
double slope(int y,int x)
{
	if(sum[x]==sum[y])return -inf;
	return double(g[x]-sum[x]*sum[x]-g[y]+sum[y]*sum[y])/double(sum[y]-sum[x]);
}
// void out(int x,int y)
// {
	// if(x==0)return;
	// if(y==1)return (void)printf("%lld ",x);
	// out(nxt[x][y-1],y-1);
	// printf("%lld ",x);
// }
signed main()
{
	scanf("%lld%lld",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	for(int j=1;j<=k;j++)
	{
		for(int i=1;i<=n;i++)
		{
			g[i]=f[i];
		}
		hd=bk=0;
		for(int i=1;i<=n;i++)
		{
			while(bk-hd>=2 && slope(q[hd],q[hd+1])<=sum[i])hd++;
			f[i]=0;
			if(hd<bk)
			{
				// nxt[i][j]=q[hd];
				f[i]=g[q[hd]]+sum[q[hd]]*(sum[i]-sum[q[hd]]);
			}
			while(bk-hd>=2 && slope(q[bk-1],q[bk-2])>=slope(i,q[bk-1]))bk--;
			q[bk++]=i;
		}
	}
	printf("%lld\n",f[n]);
	// out(nxt[n][k],k);
	return 0;
}
posted @ 2025-02-08 19:56  ikusiad  阅读(14)  评论(0)    收藏  举报