NOIP2005 过河

传送门

这道题的暴力非常容易……直接用dp[i]表示跳到i时最少踩的石子数,那么直接枚举跳跃范围转移即可。

但是这样数组开不下。我们得考虑把路径压缩一下。

我们假设选择一次跳跃距离为p,一次为p+1.这样两者必然互质,那么其中一个数的倍数必然遍历另一个的完全剩余系,所以大于p*(p+1)的数是全部都能遍历到的。

青蛙一次的跳跃距离很近,所以所有大于90的路径长度,我们可以直接压缩成90之后再跳,不过要保留所有石子,之后就像一开始暴力即可。

其实如果参照NOIP2017小凯的疑惑,你直接到71就够了,因为这两数肯定是互质的。

其实还有一种做法也可以过,就是你直接对1~10的lcs,2520取模……这样也是能过的😂

看一下代码。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<queue>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

using namespace std;
typedef long long ll;
const int M = 200005;
const int INF = 1000000009;

int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
    if(ch == '-') op = -1;
    ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
    ans *= 10;
    ans += ch - '0';
    ch = getchar();
    }
    return ans * op;
}

int l,s,t,m,dp[M],r[M],minn = INF,d[M],tot;
bool flag[M];

int main()
{
    l = read(),s = read(),t = read(),m = read();
    memset(dp,0x3f,sizeof(dp));
    dp[0] = 0;
    rep(i,1,m) r[i] = read();
    if(s == t)
    {
    rep(i,1,m) if(!(r[i]%s)) tot++;
    printf("%d\n",tot);
    return 0;
    }
    sort(r+1,r+1+m);
    rep(i,1,m) d[i] = min(r[i] - r[i-1],90);
    d[m+1] = min(l - r[m],90);
    rep(i,1,m) r[i] = r[i-1] + d[i],flag[r[i]] = 1;
    l = r[m] + d[m+1];
    rep(i,0,l)
       rep(j,s,t) dp[i+j] = min(dp[i+j],dp[i] + flag[i+j]);
    rep(i,l,l+t) minn = (minn,dp[i]);
    printf("%d\n",minn);
    return 0;
}

 

posted @ 2018-10-26 23:28  CaptainLi  阅读(205)  评论(0编辑  收藏  举报