题目大意
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
思路
本题动规不用说,问题在于怎么使路径压缩。
思路1
求出一个单位长度$x$,使得青蛙在区间$[1,x]$上从$[1,T]$开始起跳时,总会有一种跳法跳到$x$处。更严谨地说,也就是存在一个序列$K$,使得$x=sum_{i=S}^T iK_i$。我们看看$S,T$取值范围,简单粗暴地想,令$K_i=gcd(1,2,cdots i-1, i+1, cdots 10)$即可。也就是说,$x=mathrm{lcm}(1, 2, cdots, 10)$。所以对每一个间隔模个$x$即可。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX_LEN = 2100 * 100, MAX_STONE_CNT = 110, Unit = 2050, INF = 0x3f3f3f3f; int Delta[MAX_STONE_CNT], F[MAX_LEN], A[MAX_STONE_CNT]; bool HaveStone[MAX_LEN]; int Len, S, T, TotStone; int main() { scanf("%d%d%d%d", &Len, &S, &T, &TotStone); for (int i = 1; i <= TotStone; i++) scanf("%d", A + i); sort(A + 1, A + TotStone + 1); if (S == T) { int ans = 0; for (int i = 1; i <= TotStone; i++) ans += (A[i] % T == 0); printf("%d ", ans); return 0; } for (int i = 1; i <= TotStone; i++) { Delta[i] = A[i] - A[i - 1]; } Delta[TotStone + 1] = Len - A[TotStone]; for (int i = 1; i <= TotStone + 1; i++) Delta[i] %= Unit; int prevP = 0; for (int i = 1; i <= TotStone; i++) HaveStone[prevP += Delta[i]] = true; Len = prevP + Delta[TotStone + 1]; memset(F, INF, sizeof(F)); F[0] = 0; for (int i = 1; i <= Len; i++) for (int j = S; j <= T && j <= i; j++) F[i] = min(F[i], F[i - j] + HaveStone[i]); int ans = INF; for (int i = Len - T; i <= Len; i++) ans = min(ans, F[i]); printf("%d ", ans); return 0; }
思路二
求一个区间长度,使得任何大于这个区间长度的位置都可以到达。我们做过《小凯的疑惑》,知道对任意两个互质的数$a,b$,若整数$x>ab$,则总存在整数$k,t$,使得$x=ka+tb$。因为$t, t-1$互质,所以任何大于$t(t-1)$的位置都可到达。所以遇到大于$t(t-1)$的间隔便将其改为$t(t-1)$即可达到压缩效果。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX_LEN = 2100 * 100, MAX_STONE_CNT = 110, INF = 0x3f3f3f3f; int A[MAX_STONE_CNT], Delta[MAX_STONE_CNT], F[MAX_LEN]; bool HaveStone[MAX_LEN]; int Len, S, T, TotStone, Unit; int main() { scanf("%d%d%d%d", &Len, &S, &T, &TotStone); Unit = T * (T - 1); for (int i = 1; i <= TotStone; i++) scanf("%d", A + i); sort(A + 1, A + TotStone + 1); if (S == T) { int ans = 0; for (int i = 1; i <= TotStone; i++) ans += (A[i] % T == 0); printf("%d ", ans); return 0; } for (int i = 1; i <= TotStone; i++) { Delta[i] = A[i] - A[i - 1]; } Delta[TotStone + 1] = Len - A[TotStone]; for (int i = 1; i <= TotStone + 1; i++) if (Delta[i] > Unit) Delta[i] = Unit; int prevP = 0; for (int i = 1; i <= TotStone; i++) HaveStone[prevP += Delta[i]] = true; Len = prevP + Delta[TotStone + 1]; memset(F, INF, sizeof(F)); F[0] = 0; for (int i = 1; i <= Len; i++) for (int j = S; j <= T && j <= i; j++) F[i] = min(F[i], F[i - j] + HaveStone[i]); int ans = INF; for (int i = Len - T; i <= Len; i++) ans = min(ans, F[i]); printf("%d ", ans); return 0; }