这个题放到ABC的F我没有看到呀...这个应该可以过的

 

补题的时候发现这个题,这题明显dp+优化转移,就拿这个练一练 状态转移的优化叭

题意明显:

给你一个01字符串,代表n+1个石头,标号从0~n,其中’0‘为可以跳,’1‘为不可以跳,一个人从0开始跳石头,每次最多跳m个石头,也就是说他的位置 会转移到 s+x(s当前位置,x为跳的步数),问是否能精确的跳到n,如果可以输出最小需要跳的步数,否则输出-1;

9 3
0001000100
1 3 2 3

这个样例:从0跳到1,1~4,4~6,6~9 并且我们发现跳的全是0的位置,没有涉及到1

题意:

状态转移很明显:

dp[i]=(dp[k]+1,dp[i])  i-m<=k<=i-1 也就是说i只能由之前的一段区间转移而来。

So,这题的关键就不在状态转移了,而在 ==加速转移==

用一个滑动窗口一直维护 与当前相距小于m的dp的最小值直接转移即可

至于路径,其实每次转移的时候记一下之前状态就可以了,然后写个递归回溯一下。

 

AC:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
typedef long long ll;
const int maxn=1e6+5;
using namespace std;
const ll INF=1e13+7;
ll n,m;
char str[maxn];
ll dp[maxn];
ll q[maxn];
int pre[maxn];
void TraceBack(int x)
{
    if(pre[x]==-1)
    {
        printf("%d",x);
        return;
    }
    TraceBack(pre[x]);
    printf(" %d",x-pre[x]);
}
int main()
{
    memset(pre,-1,sizeof(pre));
    scanf("%lld%lld",&n,&m);
    scanf("%s",str);
    //数有n+1个
    int s=1,e=0;
    for(int i=0;i<=m;i++)
    {
        if(str[i]=='0')
            dp[i]=1;
        else
            dp[i]=INF;
        while(s<=e&&dp[i]<dp[q[e]]) e--;
        q[++e]=i;
    }
    s++;
    for(int i=m+1; i<=n; i++)
    {
        dp[i]=INF;
        if(str[i]=='0')
        {
            dp[i]=min(dp[q[s]]+1,dp[i]);
            if(!dp[i]!=INF)
                pre[i]=q[s];
        }
        while(s<=e&&dp[i]<dp[q[e]]) e--;
        q[++e]=i;
        while(s<=e&&q[s]<i+1-m)
            s++;
    }
    if(dp[n]==INF) printf("-1\n");
    else TraceBack(n);
    return 0;
}

/**

**/