/* hdu 4842(NOIP 2005 过河)之 动态规划(距离压缩) dp[i] := 到桥上的i位置,最少需要踩到的石子数。 dp[i] = min(dp[i], dp[i-j] + isStone[i]) 条件: (1) S<=j<=T (2) dp[i-j]是青蛙可以到达的点 独木桥长度L,最大为10^9,直接用转移方程,TLE;由于桥上的石子个数100,所以当石子分布在独木桥上时,是很稀疏的。 (1)S!=T 考虑,对于此题关键在于求踩到的石子数,而非跳的次数,对于两个相邻石子间的距离、起点与其相邻的最近石子间的距离、 终点与其相邻的最近石子间的距离都可以进行压缩(整体可看成平移)。 原因: 已知青蛙一次跳跃的最小距离S、以及最大距离T,当青蛙所在出发点与目标点的距离>=(T-1)*T时 (这个具体数值不精确,但是满足想达到的效果,下面有简单的证明),青蛙必然是可以到达目标点的。 这样,我们便可以利用这个来进行上面提及的距离压缩:将距离>=(T-1)*T的区间缩小(即平移),使青蛙 不再走那些一定可以到达的非石子点。 (2) S==T 无法压缩(因为步长固定了),直接判断是否(stone_pos[i] % S == 0),若是,则踩到石子数+1;否则,无须处理。
注意: 计算dp时,需要计算到dp[stone_pos[M + 1] + T - 1]。(因为有可能跳出stone_pos[M + 1])
stone_pos[M + 1]为距离压缩后,独木桥终点。
------------------------------------------------------------------------------------------ 简单证明 注:p可替换为青蛙一次跳跃的距离(T-1),p+1可替换为青蛙一次跳跃的距离(T),x、y即相应的距离跳了多少次。 Q即为所有跳跃距离加起来的和。 设:x*p + y*(p+1) = Q 则:当Q>=p*(p+1)时,x,y一定有解 证明:(来源于:ACdream群,[BNU]quailty) (p+1)*p+0*(p+1)=p(p+1) 然后x--,y++ x y Q (p) (1) p(p+1) + 1 (p-1) (2) p(p+1) + 2 ...... (1) (p) p(p+1) + p = p(p+2) (p+2)*p+0*(p+1)=p(p+2) 然后x--,y++ x y Q (p+1) (1) p(p+2) + 1 (p) (2) p(p+2) + 2 ...... (2) (p) p(p+2) + p = p(p+3) ...... 以此类推,可以到达距离起点>=p*(p+1)的任一点。 (虽然不严格,但是有助于理解)。 ------------------------------------------------------------------------------------------ */
1 #include <iostream> 2 #include <fstream> 3 #include <sstream> 4 #include <cstdlib> 5 #include <cstdio> 6 #include <cstddef> 7 #include <iterator> 8 #include <algorithm> 9 #include <string> 10 #include <locale> 11 #include <cmath> 12 #include <vector> 13 #include <cstring> 14 #include <map> 15 #include <utility> 16 #include <queue> 17 #include <stack> 18 #include <set> 19 #include <functional> 20 using namespace std; 21 typedef pair<int, int> PII; 22 typedef long long int64; 23 const int INF = 0x3f3f3f3f; 24 const int modPrime = 3046721; 25 const double eps = 1e-9; 26 const int MaxN = 11000; 27 const int MaxM = 110; 28 const char Opt[4] = { ‘+‘, ‘-‘, ‘*‘, ‘/‘ }; 29 30 int stone_pos[MaxM]; 31 int dp[MaxN]; 32 int isStone[MaxN]; 33 int L, S, T, M; 34 35 int Solve() 36 { 37 int rel = 0; 38 if (S == T) 39 { 40 for (int i = 1; i <= M; ++i) 41 { 42 if (stone_pos[i] % S == 0) 43 { 44 ++rel; 45 } 46 } 47 } 48 else 49 { 50 int disLen = (T-1)*T; 51 stone_pos[0] = 0; 52 stone_pos[M + 1] = L; 53 sort(stone_pos + 1, stone_pos + M + 1); 54 int moveLen = 0; 55 for (int i = 1; i <= (M + 1); ++i) 56 { 57 int dis = stone_pos[i] - stone_pos[i - 1] - moveLen; 58 if (dis > disLen) 59 { 60 moveLen += (dis - disLen); 61 } 62 stone_pos[i] -= moveLen; 63 64 if (i != (M + 1)) 65 { 66 isStone[stone_pos[i]] = 1; 67 } 68 } 69 dp[0] = 0; 70 for (int i = S; i <= stone_pos[M + 1] + T - 1; ++i) 71 { 72 for (int j = i - T; j <= i - S; ++j) 73 { 74 if ((j >= 0) && (-1 != dp[j])) 75 { 76 if (-1 == dp[i]) 77 { 78 dp[i] = dp[j] + isStone[i]; 79 } 80 else 81 { 82 dp[i] = min(dp[i], dp[j] + isStone[i]); 83 } 84 } 85 } 86 } 87 88 rel = INF; 89 for (int i = stone_pos[M + 1]; i <= stone_pos[M + 1] + T - 1; ++i) 90 { 91 if (-1 != dp[i]) 92 { 93 rel = min(rel, dp[i]); 94 } 95 } 96 } 97 return rel; 98 } 99 100 int main() 101 { 102 #ifdef HOME 103 freopen("in", "r", stdin); 104 //freopen("out", "w", stdout); 105 #endif 106 107 int caseNum = 0; 108 while (~scanf("%d", &L)) 109 { 110 if (caseNum != 0) 111 { 112 cout << endl; 113 } 114 fill(stone_pos, stone_pos + MaxM, 0); 115 fill(dp, dp + MaxN, -1); 116 fill(isStone, isStone + MaxN, 0); 117 scanf("%d %d %d", &S, &T, &M); 118 for (int i = 1; i <= M; ++i) 119 { 120 scanf("%d", &stone_pos[i]); 121 } 122 int rel = Solve(); 123 cout << rel; 124 ++caseNum; 125 } 126 127 128 #ifdef HOME 129 cerr << "Time elapsed: " << clock() / CLOCKS_PER_SEC << " ms" << endl; 130 _CrtDumpMemoryLeaks(); 131 #endif 132 return 0; 133 }
时间: 2024-10-20 16:09:07