luogu1052 过河

题目大意

  在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点: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\n", 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\n", 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\n", 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\n", ans);
    return 0;
}

  

posted @ 2018-08-27 16:08  headboy2002  阅读(120)  评论(0编辑  收藏  举报