【解题报告】洛谷P1725 琪露诺

【解题报告】洛谷P1725 琪露诺

题目链接

https://www.luogu.com.cn/problem/P1725

思路

这道题目首先一看就要用动态规划

我们设 \(f[i]\) 表示走到 \(i\) 这个位置可以得到的最大的冰冻指数

并可以简单地得到状态转移方程

\[f[j]=max\{f[i]\}+a[i],i+l \le j \le min(i+r,n) \]

然后我们打下去就可以得到60pts了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
#define int long long
using namespace std;
const int maxn=200005;
int n,l,r;
int a[maxn];
int f[maxn];
signed main()
{
	memset(f,-0x3f,sizeof(f));
	cin>>n>>l>>r;
	for(int i=0;i<=n;i++) 
	cin>>a[i];
	f[0]=0;
	for(int i=0;i<=n;i++)
	{
		for(int j=i+l;j<=i+r&&j<=n;j++)
			f[j]=max(f[j],f[i]+a[j]);
	}
	int ans=-1e9;
	for(int i=n-(r-l+1);i<=n;i++)
	ans=max(f[i],ans);
	cout<<ans<<'\n'; 
	return 0;
}

接着我们观察状态转移方程 \((1)\) ,发现这个状态转移方程实际上是要找一个最大的 \(f[j]\) 并且 \(j\) 属于一个区间

所以我们很容易就想到了滑动窗口的思想,单调队列

单调队列是什么呢?

如果队首超出了区间,那么队首弹出;如果队尾小于新要加进来的数字,那么队尾肯定不是要的最大值,所以把队尾弹出就可以了,然后最后要找的最大值肯定在队尾,这是单调的,可以证明

然后我们就可以写出来了

我写出来之后为了保证前 60pts 的分数正确,写了一个subtask,这个小技巧可以保证你能得到的分数一定会得到

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
#define int long long
using namespace std;
const int maxn=200005;
int n,l,r;
int ans=-1e9;
int a[maxn];
int f[maxn];
int q[maxn],head,tail;
void task1()
{
	head=1,tail=0; 
	for(int i=l;i<=n;i++)
	{
		while(head<=tail&&q[head]<i-r) head++;
		while(head<=tail&&f[q[tail]]<f[i-l]) tail--;
		q[++tail]=i-l;
		f[i]=f[q[head]]+a[i];
		if(i+r>n)
		ans=max(f[i],ans);
	}
	cout<<ans<<'\n'; 
}
void task2()
{
	memset(f,-0x3f,sizeof(f));
	f[0]=0;
	for(int i=0;i<=n;i++)
	{
		for(int j=i+l;j<=i+r&&j<=n;j++)
			f[j]=max(f[j],f[i]+a[j]);
	}
	int ans=-1e9;
	for(int i=n-(r-l+1);i<=n;i++)
	ans=max(f[i],ans);
	cout<<ans<<'\n'; 
}
signed main()
{

	cin>>n>>l>>r;
	for(int i=0;i<=n;i++) 
	cin>>a[i];
	f[0]=0;
	if(n<=10000) task2();
	else task1();
	return 0;
}
posted @ 2021-10-12 19:37  wweiyi  阅读(20)  评论(0编辑  收藏  举报
js脚本