DP 开宝箱(简单DP)
题目:
你确定我是来开宝箱不是来比赛的吗??艾玛,这是哪,天啊,伦家竟然穿越了。
摆在眼前的是1个n*m(0< n<=100&&0< m<=100)的宝箱矩阵,而你就在这个矩阵的左上角也就是入口处。冲啊,开宝箱去,然而脚不听话啊,你的脚失去了向左走与向上走(方向是相对于整个地图,换句话说不是通过你脸的朝向来决定方向,你只能↓和→)的能力。(你:这设定真奇怪啊 。WGX大神:让你四处走还不把我的宝箱全开了)。
不管了开宝箱要紧。假设每个宝箱有一个数字V,如果是正数那么你获得V个金币,如果是负数,哈哈,不好意思,扣去相应个金币。(哭晕在厕所)。
那么问题来了:
如果你在已知宝箱的奖励的情况下,请选择一条路线,这条路线能让你获得最多的金币,当然所有走过的宝箱必须打开,不能说因为是负数你就选择不去打开它,并且出口(左上)和入口(右下)的那两个宝箱是必经点,也必须打开。
你的任务只是求出该路线获得的金币值就行。
题解:
很明显状态转移方程为 f[i][j]=max(f[i-1][j],f[i][j-1])+b[i][j];
记得要DP数组初值赋值为无穷小
代码:
#include<stdio.h>
#include<limits.h>
int b[101][101];
int f[101][101];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m,i,j,k,kk;
scanf("%d%d",&n,&m);
for(i=0;i<=n+1;i++)
{
for(j=0;j<=m+1;j++)
f[i][j]=INT_MIN;
}
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
scanf("%d",&b[i][j]);
}
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
if(i==1 && j==1)
f[1][1]=b[1][1];
else
f[i][j]=max(f[i-1][j],f[i][j-1])+b[i][j];
}
}
printf("%d\n",f[n][m]);
}
return 0;
}
DP 最长上升子序列(板子)
#include<stdio.h>
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
int a[1000050];
int ans[1000050];
int main()
{
int n,i;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
ans[1]=a[1];
int len=1;
for(i=2;i<=n;i++)
{
if(a[i]>ans[len])
ans[++len]=a[i];
else
{
int num=upper_bound(ans+1,ans+len+1,a[i])-ans;
ans[num]=a[i];
}
}
printf("%d\n",len);
return 0;
}
DP 最长公共子序列(板子)
复杂度O(n*n)
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int> p;
typedef long long LL;
char s1[105],s2[105];
int dp[105][105];
int main()
{
scanf("%s",s1);
scanf("%s",s2);
int l1=strlen(s1);
int l2=strlen(s2);
for(int i=0;i<=l1;i++)
for(int j=0;j<=l2;j++)
{
if(s1[i]==s2[j])
dp[i+1][j+1]=dp[i][j]+1;
else
dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
}
printf("%d\n",dp[l1][l2]);
return 0;
}
复杂度O(n*logn)
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const int maxn = 1501 ;
vector<int> location[26] ;
int c[maxn*maxn] , d[maxn*maxn] ;
inline int get_max(int a,int b) { return a > b ? a : b ; }
//nlogn 求lcs
int lcs(char a[],char b[])
{
int i , j , k , w , ans , l , r , mid ;
for( i = 0 ; i < 26 ; i++) location[i].clear() ;
for( i = strlen(b)-1 ; i >= 0 ; i--) location[b[i]-'a'].push_back(i) ;
for( i = k = 0 ; a[i] ; i++)
{
for( j = 0 ; j < location[w=a[i]-'a'].size() ; j++,k++) c[k] = location[w][j] ;
}
d[1] = c[0] ; d[0] = -1 ;
for( i = ans = 1 ; i < k ; i++)
{
l = 0 ; r = ans ;
while( l <= r )
{
mid = ( l + r ) >> 1 ;
if( d[mid] >= c[i] ) r = mid - 1 ;
else l = mid + 1 ;
}
if( r == ans ) ans++,d[r+1] = c[i] ;
else if( d[r+1] > c[i] ) d[r+1] = c[i] ;
}
return ans ;
}
int main()
{
char a[maxn] , b[maxn] ;
while (~scanf("%s%s",a,b))
{
printf("%d",lcs(a,b));
}
}
DP 最长公共子序列(板子)
复杂度O(n*n)
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int> p;
typedef long long LL;
char s1[105],s2[105];
int dp[105][105];
int main()
{
scanf("%s",s1);
scanf("%s",s2);
int l1=strlen(s1);
int l2=strlen(s2);
for(int i=0;i<=l1;i++)
for(int j=0;j<=l2;j++)
{
if(s1[i]==s2[j])
dp[i+1][j+1]=dp[i][j]+1;
else
dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
}
printf("%d\n",dp[l1][l2]);
return 0;
}
复杂度O(n*logn)
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const int maxn = 1501 ;
vector<int> location[26] ;
int c[maxn*maxn] , d[maxn*maxn] ;
inline int get_max(int a,int b) { return a > b ? a : b ; }
//nlogn 求lcs
int lcs(char a[],char b[])
{
int i , j , k , w , ans , l , r , mid ;
for( i = 0 ; i < 26 ; i++) location[i].clear() ;
for( i = strlen(b)-1 ; i >= 0 ; i--) location[b[i]-'a'].push_back(i) ;
for( i = k = 0 ; a[i] ; i++)
{
for( j = 0 ; j < location[w=a[i]-'a'].size() ; j++,k++) c[k] = location[w][j] ;
}
d[1] = c[0] ; d[0] = -1 ;
for( i = ans = 1 ; i < k ; i++)
{
l = 0 ; r = ans ;
while( l <= r )
{
mid = ( l + r ) >> 1 ;
if( d[mid] >= c[i] ) r = mid - 1 ;
else l = mid + 1 ;
}
if( r == ans ) ans++,d[r+1] = c[i] ;
else if( d[r+1] > c[i] ) d[r+1] = c[i] ;
}
return ans ;
}
int main()
{
char a[maxn] , b[maxn] ;
while (~scanf("%s%s",a,b))
{
printf("%d",lcs(a,b));
}
}
** 免费馅饼(简单DP)**
题目:
都说天上不会掉馅饼,但有一天gameboy正走在回家的小径上,忽然天上掉下大把大把的馅饼。说来gameboy的人品实在是太好了,这馅饼别处都不掉,就掉落在他身旁的10米范围内。馅饼如果掉在了地上当然就不能吃了,所以gameboy马上卸下身上的背包去接。但由于小径两侧都不能站人,所以他只能在小径上接。由于gameboy平时老呆在房间里玩游戏,虽然在游戏中是个身手敏捷的高手,但在现实中运动神经特别迟钝,每秒种只有在移动不超过一米的范围内接住坠落的馅饼。现在给这条小径如图标上坐标:
为了使问题简化,假设在接下来的一段时间里,馅饼都掉落在0-10这11个位置。开始时gameboy站在5这个位置,因此在第一秒,他只能接到4,5,6这三个位置中其中一个位置上的馅饼。问gameboy最多可能接到多少个馅饼?(假设他的背包可以容纳无穷多个馅饼)
Input
输入数据有多组。每组数据的第一行为以正整数,表示有n个馅饼掉在这条小径上。在结下来的n行中,每行有两个整数x,T(表示在第T秒有一个馅饼掉在x点上。同一秒钟在同一点上可能掉下多个馅饼。n=0时输入结束。
Output
每一组输入数据对应一行输出。输出一个整数m,表示gameboy最多可能接到m个馅饼。
提示:本题的输入数据量比较大,建议用scanf读入,用cin可能会超时。
题解:
DP[i][j]代表i时刻在j位置接到的馅饼。
因为不知道最好停留的位置,所以时间逆序DP。
特判一下0位置,不然数组会越界。
代码:
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<iostream>
using namespace std;
int dp[100005][12];
int main()
{
int n,i,j;
while(~scanf("%d",&n))
{
if(n==0)break;
int time=-1;
memset(dp,0,sizeof(dp));
for(i=0;i<n;i++)
{
int x,t;
scanf("%d%d",&x,&t);
dp[t][x]++;
time=max(time,t);
}
for(i=time-1;i>=0;i--)
{
for(j=0;j<11;j++)
{
if(j==0)
dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+dp[i][j];
else
dp[i][j] = max(max(dp[i+1][j],dp[i+1][j+1]),dp[i+1][j-1])+dp[i][j];
}
}
printf("%d\n",dp[0][5]);
}
return 0;
}
HDU 2089 不要62 (数位DP)
大神的操作 当板子用…
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
typedef long long ll;
int a[20];
int dp[20][2];
int dfs(int pos,int pre,int sta,bool limit)
{
if(pos==-1) return 1;
if(!limit && dp[pos][sta]!=-1) return dp[pos][sta];
int up=limit ? a[pos] : 9;
int tmp=0;
for(int i=0;i<=up;i++)
{
if(pre==6 && i==2)continue;
if(i==4) continue;//都是保证枚举合法性
tmp+=dfs(pos-1,i,i==6,limit && i==a[pos]);
}
if(!limit) dp[pos][sta]=tmp;
return tmp;
}
int solve(int x)
{
int pos=0;
while(x)
{
a[pos++]=x%10;
x/=10;
}
return dfs(pos-1,-1,0,true);
}
int main()
{
int le,ri;
//memset(dp,-1,sizeof dp);可优化
while(~scanf("%d%d",&le,&ri) && le+ri)
{
memset(dp,-1,sizeof dp);
printf("%d\n",solve(ri)-solve(le-1));
}
return 0;
}
状压DP
https://mp.csdn.net/mdeditor/81407630
P1280 尼克的任务(DP)
这道题虽然不难,但是这个状态方程对于我来说还是很难想到。这道题要倒着DP,因为顺序的话不知道结束的状态是什么,所以考虑的因素很多。倒着的话,就两种情况。
一:当前无任务,休息一秒
二:当前有任务,考虑做不做
直接上代码。
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
using namespace std;
int a[100005]; //记录开始的时间
int b[100005]; //记录结束的时间
int dp[10005];
int main()
{
int n,t,i,x,y;
scanf("%d%d",&t,&n);
for(i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
a[i]=x;
b[i]=y;
}
int num=n;
for(i=t;i>=1;i--)
{
if(a[num]!=i) dp[i]=dp[i+1]+1;
//当前没有任务,所以休息一秒
else
{
while(a[num]==i)
{
dp[i]=max(dp[i],dp[i+b[num]]);
//当前有任务,考虑要不要做。
num--;
}
}
}
printf("%d\n",dp[1]);
return 0;
}
洛谷P1006 传纸条(DP)
很经典
题目描述
小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个 m 行 n 列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标 (1,1 ),小轩坐在矩阵的右下角,坐标 (m,n) 。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。
在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。
还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用 0 表示),可以用一个 0-100的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这 22 条路径上同学的好心程度之和最大。现在,请你帮助小渊和小轩找到这样的 2 条路径。
输入输出格式
输入格式:
输入文件,第一行有 2 个用空格隔开的整数 m 和 n ,表示班里有 m 行 n 列。
接下来的 m 行是一个 m×n 的矩阵,矩阵中第 i 行 j 列的整数表示坐在第 i行 j 列的学生的好心程度。每行的 n个整数之间用空格隔开。
输出格式:
输出文件共一行,包含一个整数,表示来回 2 条路上参与传递纸条的学生的好心程度之和的最大值。
输入输出样例
输入样例#1: 复制
3 3
0 3 9
2 8 5
5 7 0
输出样例#1: 复制
34
题解:
感觉很难理解…直到看到了这个题解。
因为是从上方和从下方传纸条,为了方便,我们相当于从左上角连续传两张纸条,路径不重复,效果相同。
从左上来看的话就只能向右或向下传纸条。
那么两张纸条在过程中就一定在一条斜线上,而在一条斜线上纵坐标与横坐标相加相等。
在如图的斜线中,两个点的和都为3.
首先重要的就是三维F数组。
第一维度维护的是在传的过程中纵坐标与横坐标的和。
在同一斜线上,剩下表示两个点的从坐标就可以表示这两个点的位置。
第二维度维护的是相对在左边的点的纵坐标。
第三维度维护的是相对在右边的点的纵坐标。
当查询一个情况时,只有四种情况可以到他
F[sum][i][j]=max{F[sum-1][i][j]+F[k-1][i][j-1]+F[k-1][i-1][j]+F[k-1][i-1][j-1];
最后再加上a数组里存的两个点的好感度即可
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=60;
int a[maxn][maxn];
int F[2*maxn][maxn][maxn];
int main()
{
int m,n;
scanf("%d%d",&m,&n);
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
//F[sum][i][j]=max{F[sum-1][i][j]...
memset(F,-1,sizeof(F));//赋初值为-1 (原因在后面)
F[2][1][1]=0;//最初的点,在左上角,好感度为0
for(int k=3;k<m+n;k++)
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
{
int s=F[k][i][j];
if(F[k-1][i][j]>s)s=F[k-1][i][j];
if(F[k-1][i-1][j]>s)s=F[k-1][i-1][j];
if(F[k-1][i][j-1]>s)s=F[k-1][i][j-1];
if(F[k-1][i-1][j-1]>s)s=F[k-1][i-1][j-1];
if(s==-1)continue;//当s为-1时,说明四种情况都不能到该点,故不存在。
F[k][i][j]=s+a[k-i][i]+a[k-j][j];//该点的值为最大的前一个值与当前F[k][i][j]表示两点的值的和。
}
printf("%d",F[m+n-1][n-1][n]);//因为i永远小于j,所以右下角的点不会求到,
//但是到右下角只有一种情况,就是在右下角的上面和右下角的左边,直接输出就好了。
return 0;
}
P1387 最大正方形(DP)
题目:
在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长。
输入输出样例
输入样例#1: 复制
4 4
0 1 1 1
1 1 1 0
0 1 1 0
1 1 0 1
输出样例#1: 复制
2
题解:
只有当前位置为1时,正方形长度才可以更大。而要使得正方形最大,左,上,左上都为一,否则为他们三个里面最小的那个加1.所以状态转移方程为:
f[i][j]=min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1])+1;
代码:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
int a[101][101];
int f[101][101];
int main()
{
int n,m,i,j,maxx=-1;
scanf("%d%d",&n,&m);
memset(f,0,sizeof(f));
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
if(a[i][j]==1)
f[i][j]=min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1])+1;
maxx=max(maxx,f[i][j]);
}
printf("%d\n",maxx);
return 0;
}
P1025 数的划分(DP)
题目描述
将整数n分成k份,且每份不能为空,任意两个方案不相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5;
1,5,1
5,1,1.
问有多少种不同的分法。
输入输出格式
输入格式:
n,k
输出格式:
1个整数,即不同的分法。
输入输出样例
输入样例#1: 复制
7 3
输出样例#1: 复制
4
说明
四种分法为:
1,1,5;
1,2,4;
1,3,3;
2,2,3.
题解
首先感叹真的是弱。这个DP题状态转移方程真的是想不出来。直到我看到了这道题的另一个题目。。
这题其实是排列组合里的题,可以把一个数值为n的数当做n个小球,划分的份数k当做k个盒子,那么本题可以转化为“将n个小球放到k个盒子中,小球之间与盒子之间没有区别,并且最后的结果不允许空盒”
将n个小球放到k个盒子中的情况总数 =
a.至少有一个盒子只有一个小球的情况数
+b.没有一个盒子只有一个小球的情况数
这样进行划分是因为这种分类可以使a和b都有能写出来的表达式:
a.因为盒子不加区分,那么1的情况数与“将n-1个小球放到k-1个盒子中”的情况数一样
b.没有一个盒子只有一个小球,那么把每个盒子中拿出来一个小球,对应的是“把(n-k)个小球放到k个盒子中的情况数”
然后将上面的思路化为动态转移方程:
设f[n,k]代表将n个小球放到k个盒子中且没有空盒的情况,那么f[n,k] = f[n-1,k-1] + f[n-k,k]
而当k=1时只有1种方法(小球全部放进1个盒子)
代码
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
int dp[205][10];
int main()
{
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
dp[i][1]=1;
dp[i][0]=1;
}
for(int j=2;j<=k;j++)
{
dp[1][j]=0;
dp[0][j]=0;
}
for(int i=2;i<=n;i++)
{
for(int j=2;j<=k;j++)
{
if(i>j)
dp[i][j]=dp[i-1][j-1]+dp[i-j][j];
else
dp[i][j]=dp[i-1][j-1];
}
}
printf("%d\n",dp[n][k]);
return 0;
}
洛谷P1052 过河(DP+路径压缩离散化)
题目描述
在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,…,L(其中LL是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。
题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。
输入输出格式
输入格式:
第一行有1个正整数L(1 ~10^9)
),表示独木桥的长度。
第二行有3个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离及桥上石子的个数,
第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。
输出格式:
一个整数,表示青蛙过河最少需要踩到的石子数。
输入输出样例
输入样例#1: 复制
10
2 3 5
2 3 5 6 7
输出样例#1: 复制
2
题解
首先说状态转移方程
这是一个比较简单方程式。
首先设f[i]为在i点上的最少踩石子数,则在前面(i-s)到(i-t)的点都可以改变i点的值,因此我们可以取f[i-s]-f[i-t]之中的最小值,另外如果有石头就加上1,如果没有就不加值,这里我们直接用flag[i]表示该点有无石头(有则为1,无则为0)。
因此我们可以写出状态转移方程式 f[i]=min(f[i-j]+flag[i]|s<=j<=t)
然后因为n的数据太大,数组根本存不下,所以这个时候就要考虑把数据离散化,也就是路径压缩短。
因为n为1e9,但是s,t的范围是1-10,并且m<100,所以中间其实很多点都是没有石头的。然后我们不知道每一步怎么走,所以压缩的时候,如果两石头间距大于1-10的最小公倍数即2520,就求余2520,然后把l压缩短,就可以了。
最后注意一点,最后dp的结果不是dp[l],因为走到l位置不一定是最优解,所以要取l到l+r-1直接的最优解。
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<iostream>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define maxn 1000500
using namespace std;
typedef unsigned long long LL;
int a[105];//地图
int d[105];//间距
int dp[500005];//dp
int dis[500005];//判断该点是否有石头
int main()
{
int l,s,t,m;
scanf("%d%d%d%d",&l,&s,&t,&m);
for(int i=1;i<=m;i++)
scanf("%d",&a[i]);
sort(a+1,a+1+m);
for(int i=1;i<=m;i++)
d[i]=(a[i]-a[i-1])%2520;//要对1~10的最小公倍数取余,压缩路径的核心
for(int i=1;i<=m;i++)
{
a[i]=a[i-1]+d[i];
dis[a[i]]=1;
}
l=a[m];
for (int i=0;i<=l+t;i++)
dp[i]=m; //f[i]表示到位置i最少能踩到的石子数
dp[0]=0;
for(int i=1;i<l+t;i++)
{
for(int j=s;j<=t;j++)
{
if(i>=j)
dp[i]=min(dp[i],dp[i-j]); //状态转移方程
}
dp[i]+=dis[i];
}
int ans=m;
for(int i=l;i<l+t;i++)
{
ans=min(ans,dp[i]);
}
printf("%d\n",ans);
return 0;
}