现在的位置: 首页 > 综合 > 正文

NOIP05 过河(状态压缩DP)

2013年03月03日 ⁄ 综合 ⁄ 共 2126字 ⁄ 字号 评论关闭

题目链接 http://www.rqnoj.cn/Problem_17.html
转移方程容易给出dp[i]表示青蛙到i之前的前一个位置至少好需要经过几个石子才能到达终点

当0≤i≤L时:dp[i]=min{dp[i+j]}+bridge[i](S≤j≤T)

当i>L时:    dp[i]=0
状态转移方程的时间复杂度为O(n),n最大为L*(S -T)。题目中给出的数据过于变态,L达到了10^9,必然会出现超时的情况。
一. S=T的情况直接mod就可以了。
二. S!=T
1 题目中给出的L巨大,但是最多存在100颗石子,这说明在这条路径上肯定有好多段距离之间并不存在石子,但是我们的动归方程把大段不存在石子的距离也进行了计算,造成了时间上不必要的浪费。现在我们想应该如何处理大段不存在石子的距离以减少不必要的状态,减少时间开销。
2 我们是不是可以去掉中间大段不存在石子的距离进行空间压缩呢? 如果可以我们最小能压缩到多少呢? 为什么不能继续压缩呢?假如 1 ~ 100的距离之间只有100的位置上存在一颗石子,S=4  T=5。此时青蛙所有可能到达的位置为
4   5
8   9   10 
12  13  14  15
16  17  18  19  20
21  22  23  24  25 26
从12这个位置开始以后所有的位置都有可能到达形成连续区间,表明12 ~ 99所有的位置都有可能到达,这些位置对于确定100这个点能否到达做出的贡献都是一样的。所以dp[13] ~ dp[99]计不计算都无所谓,可以用dp[12]来代替,我们此时回答了第一个问题即可以去掉中间大段不存在石子的距离。此时我们考虑100这个石子到不到的问题,就跟考虑能不能到到达12这个问题一样了。第二个问题我们最小可以压缩到连续空间开始时的数值12。第三个问题如果我们在12的基础上进行继续压缩,我们就不可能找出一个点完全代替后面的点,因为后边出现了不可能到达的点了。三个问题完全解决。
此时还有个疑问我们该如何确定最小压缩距离呢,这个题目我们是笔算出来的如果S T换做其他数值呢,首先我们可以想一下S ~ T这个区间由T与T-1确定的压缩区间是最大的,因为其他的不是数值较小就是区间太大。已知一结论  Px+(P+1)y=Q ,x y P Q均为整数,其中P Q 为非负整数,当Q>=P*(P-1)时,x y必有非负解,即P*(P-1)以后所有的数值都可得到。
证明:
x=x0+(p+1)t  y=y0-pt;取适当的t,使0=<x<= P,则当Q>P*(P-1)-1时有(P+1)y=Q-Px>=P*(P-1)-Px>=P*(P-1)-P*P>=-P >-(P+1);则y>-1也为非负数,证明成立。
应用到题目中T最大为10,则可以确定的最小压缩空间为9*(9-1)=72,从72开始就会出现区间完全连续。
我尽力叙述的清楚了,如果还有不懂得请留言。

#include <iostream>
#include <string>
#include <algorithm>
#define MAX_stone 102
#define MAX_sapce 72  // 最小压缩空间 
#define MAX 10000

int L, S, T, M, ans = 0, Len = 0;
int stone_pos[MAX_stone], bridge[MAX], flag[MAX], dp[MAX];

int comp(const void *a, const void *b)
{
	return *(long *)a - *(long *)b;
}

int compute(int i)
{
	if(i >= Len) return 0;
	if(flag[i]) return dp[i];
	flag[i] = 1; dp[i] = INT_MAX;
	for(int j = S; j <= T; j++)
	{
		if(dp[i] > compute(i + j))
		{
			dp[i] = compute(i + j);
		}
	}
	dp[i] += bridge[i];
	return dp[i];
}

int main()
{
	memset(stone_pos, 0, sizeof(stone_pos));
	memset(bridge, 0, sizeof(bridge));
	memset(flag, 0, sizeof(flag));
	scanf("%d%d%d%d", &L, &S, &T, &M);
	if(S == T)                        //青蛙只能跳固定距离的情况 
	{
		for(int i = 1; i <= M; i++)
		{
			scanf("%d", &stone_pos[i]);
			if(0 == stone_pos[i] % S) ans++;
		}
	}
	else							// 不是固定距离时进行判断,看能不能状态压缩 
	{
		for(int i = 1; i <= M; i++) scanf("%d", &stone_pos[i]);
		qsort(stone_pos, M + 1, sizeof(stone_pos[0]), comp);
		for(int i = 1; i <= M; i++)
		{
			if(stone_pos[i] - stone_pos[i - 1] <= MAX_sapce) //判断是否可以进行压缩 
			{
					Len += stone_pos[i] - stone_pos[i - 1];
			}
			else Len += MAX_sapce;
			bridge[Len] = 1;
		}
		ans = compute(0);
	}
	printf("%d\n", ans);
} 

 

抱歉!评论已关闭.