Luogu P1052 过河

思路

动态规划不用说,而且这道题的转移方程非常简单,连我都能推出来。难就难在怎么去优化空间和时间。因为$\large{L\le 10^9}$,直接开数组的话不仅空间会炸的连渣都不剩,而且枚举的时候时间也会炸的连渣都不剩。看了题解后,我感到了世界对我的恶意我好弱鸡。用离散化的方法缩小复杂度。

输入之后将石头的位置排序。首先保证它们是递增的,那么相邻的两个石头之间肯定没有其他石头了。所以相邻的两个石头坑定能通过$\large{tx+(a[i]-a[i-1])\%t}$走出来,但是我们根本不用考虑x,直接看做$1$,那么数组的大小数组的大小就变成了$\large{2*t*m}$。明显的小了不少。

 

状态转移方程

$$\large{dp[i] = min(dp[i], dp[i-j]+flag[i-j]), s\le j\le t}$$

 

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

int m, L, s, t, dp[2500], a[105], cnt, ans = 2147483647;
bool flag[2500];

int main() {
	scanf("%d%d%d%d", &L, &s, &t, &m);
	memset(dp, 0x3f, sizeof(dp));
	for(int i=1; i<=m; i++) {
		scanf("%d", &a[i]);
	}
	a[m+1] = L;
	sort(a, a+2+m);
	for(int i=1; i<=m+1; i++) {
		if(a[i] - a[i-1] > t)
			cnt += (a[i]-a[i-1]) % t + t;
		else cnt += a[i]-a[i-1];
		flag[cnt] = true;
	}
	flag[cnt] = 0, flag[0] = 0, dp[0] = 0;
	for(int i=1; i<=cnt+t+1; i++) {
		for(int j=s; j<=t && i-j>=0; j++) {
			dp[i] = min(dp[i], dp[i-j]+flag[i-j]);
		}
	}
	for(int i=cnt; i<=cnt+t; i++) {
		ans = min(dp[i], ans);
	}
	printf("%d", ans);
}

  

posted @ 2018-07-19 08:39  Mystical-W  阅读(124)  评论(0编辑  收藏  举报