P1052 过河(状态压缩dp)

https://www.luogu.org/problemnew/show/P1052

题解

很容易得出状态转移方程
dp[i] = dp[i-k]+stone[i], s <= k <= t
其中i代表走到i时踩了多少个石头。
对于百分之30的数据,直接这样搞就可以了。
但是数据范围是10^9,内存根本不够。仔细观察可以发现,最多只有100个石头,每次跳的距离最大10个单位,很明显里面有很大的一段路程是没有石头的。这里需要一个小知识:lcm(1,2,3,4,5,6,7,8,9,10) = 2520。即从0开始不论怎么跳,最终一定会跳到2520。所以如果两个石头之间的距离超过2520,那就可以对其取余,例如0跳到2521,无论怎么跳你都会跳到2520,那么就相当于从0跳到1。这样路径压缩起来的大小不超过100*2520。

代码

#include 
using namespace std;
const int maxn = 250000+100;
const int INF = 0x3f3f3f;
// dp[i] = dp[i-k]+stone[i]; s <= k <= t
int a[maxn],d[maxn],stone[maxn],dp[maxn];
int main() {
	int L,S,T,M;
	scanf("%d", &L);
	scanf("%d%d%d", &S, &T, &M);
	for(int i = 1; i <= M; ++i) 
		scanf("%d", &a[i]);
	sort(a+1,a+1+M);
	for(int i = 1; i <= M; ++i) 
		d[i] = (a[i]-a[i-1])%2520;
	for(int i = 1; i <= M; ++i) {
		a[i] = a[i-1]+d[i];
		stone[a[i-1]+d[i]] = 1;
	}
	int r = a[M];
	memset(dp, INF, sizeof dp);
	dp[0] = 0;
	int ans = INF;
	for(int i = 1; i <= r+T; ++i) {
		for(int j = S; j <= T; ++j) {
			if(i-j >= 0)
				dp[i] = min(dp[i], dp[i-j]+stone[i]);
			if(i >= r)
				ans = min(ans, dp[i]);
		}
	}
	cout << ans << endl;
	return 0;
}

你可能感兴趣的:(动态规划)