E. Sleeping Schedule


E. Sleeping Schedule

  • 看我博客我有没看懂的地方,或者其他疑问,可以加我qq和我交流~我会及时解答
  • qq:1244536605

标签

  • 线性dp

简明题意

  • 给定n长的数组a[],再给定h,l,r。
  • 现在有一个sum,依次考虑数组a中的每一个数a[i],可以让sum+=a[i]或者sum+=a[i]-1。每次加完后,sum%h,问最多多少次使得sum属于[L,R]范围。n、h均在2000内。

思路

  • 先设dp[i]表示考虑到数组的第i位时的最多满足要求个数。
  • 想想转移,根据题目的要求,肯定要从i-1转移过来。这样显然会有后效性,因为i这里选择了a[i]-1,可能导致后面怎么选都选不到[L,R]范围内的。所以我们要取消掉这个后效性,让每次的选择不会影响后面的选择。
  • 通常的做法就是把产生后效性的因素放进状态里,你会发现h<2000,那么放进状态里是可行的。那么我们设dp[i][j]表示第i天,且加完a[i]后sum=j的最优解。
  • 想想转移。很简单,dp[i][j]=max(dp[i-1][j-a[i]],dp[i-1][j-a[i]+1])+1。

注意事项


总结

  • 有后效性时,就把后效性写进状态里,后效性就没有了。

AC代码

#pragma GCC optimize(2)
#include
#include
#include
#include 
#include
#include
#include
#include	
#include
#include
#include
using namespace std;

const int maxn = 2e3 + 10;

int a[maxn], dp[maxn][maxn];

void solve()
{
	int n, h, l, r;
	cin >> n >> h >> l >> r;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	memset(dp, -0x3f3f3f, sizeof dp);

	dp[0][0] = 0;
	int ans = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 0; j < h; j++)
			ans = max(ans, dp[i][j] = max(dp[i - 1][(j - a[i] + h) % h], dp[i - 1][(j - a[i] + 1 + h) % h]) + ((j >= l && j <= r) ? 1 : 0));
	cout << ans;
}
	
int main()
{
	//freopen("Testin.txt", "r", stdin);
	solve();
	return 0;
}

你可能感兴趣的:(#,线性dp)