这个题放到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;
}
/**
**/