CF1324E Sleeping Schedule 题解

博客介绍了CF1324E题目的解决方案,这是一个关于求解前缀和模h值在[l,r]区间内最大数量的问题。通过动态规划的方法,用fi,j表示前i个数中前缀和模h等于j的最大答案。不选择第i个数时,fi,j更新为fi−1,j−ai,选择时更新为fi−1,j−ai+1,最后加上判断条件(l≤j and j<=r)来确保答案在给定范围内。" 104175864,6289648,富爸爸的财务智慧:从为钱工作到让钱工作,"['理财', '投资', '财务规划', '个人经济', '资产配置']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

博客园同步

原题链接

简要题意:

每次可以将 a i a_i ai 1 1 1 或不变。求让 a i a_i ai 的前缀和 % h \% h %h 的值在 [ l , r ] [l,r] [l,r] 区间中的最多的个数。

E题是个水dp,也不怎样

f i , j f_{i,j} fi,j 表示前 i i i 个数中, ( ∑ k = 1 i a k ) % h = j \bigg ( \sum_{k=1}^{i} a_k \bigg ) \% h = j (k=1iak)%h=j 的最大答案。

显然,我们从第 i i i 个数入手。(下标出现负数的,在代码中均处理;转移方程中保留)

如果不选,那么 f i , j = f i − 1 , j − a i f_{i,j} = f_{i-1,j-a_i} fi,j=fi1,jai.

如果选,那么 f i , j = f i − 1 , j − a i + 1 f_{i,j} = f_{i-1,j-a_i+1} fi,j=fi1,jai+1.

最后, f i , j ← f i , j + ( l ≤ j   and   j < = r ) f_{i,j} \gets f_{i,j} + (l \leq j \space \texttt{and} \space j<=r) fi,jfi,j+(lj and j<=r)

这是因为,如果当前的这个前缀和在该范围,也算一个答案。

所以:

f i , j = { 0 , i = 0   and   j = 0 max ⁡ f i − 1 , j − a i , f i − 1 , j − a i + 1 f_{i,j} = \begin{cases} 0 , i=0 \space \texttt{and} \space j=0 \\ \max{f_{i-1 , j-a_i} , f_{i-1,j-a_i+1}} \\ \end{cases} fi,j={0,i=0 and j=0maxfi1,jai,fi1,jai+1

防止出现下标负数 x x x ,这样处理:

x ← ( x + h ) % h x \gets (x+h) \%h x(x+h)%h

如果 x x x 是正数,那 + h +h +h 不影响答案;如果 x x x 是负数,那 + h +h +h 变为正数,答案也正确。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int N=2e3+1;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

int n,h,l,r,ans=0;
int a[N],f[N][N];

int main(){
	n=read(),h=read(),l=read(),r=read();
	for(int i=1;i<=n;i++) a[i]=read();
	memset(f,-63,sizeof(f)); //预处理为极小值
	f[0][0]=0;
	for(int i=1;i<=n;i++)
	for(int j=0;j<h;j++)
		f[i][j]=max(f[i-1][(j-a[i]+h)%h],f[i-1][(j-a[i]+1+h)%h])+(l<=j && j<=r);
	for(int i=0;i<h;i++) ans=max(ans,f[n][i]); //将 1~n 的答案取最大值
	printf("%d\n",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值