首先这个题DP应该是挺容易看出来的,就是一个线性的东西,后面的可以从前面的转移。
然后既然想到了DP,可以决定一下DP的状态,很显然可以定dp[i]为到位置i的时候最少踩多少个石子。
再然后就是转移方程了,对于s<=j<=t,并且i>=j,dp[i]=min(dp[i],dp[i-j]+vis[i]]),其中vis[i]表示i号位是不是一个石子。
这样DP部分是完成了,但是有一个很有意思的点就是,这个l好大,竟然到了1e9。可是呢,s,t,m这些都好小,最大才100。
做过离散化相关的题的人都会发现,应该是需要离散化一下了。
如果不懂离散化也没问题,下面也能讲得清楚:
假如你在0号位,下一个石子在55号位,t是10。那可以发现肯定是这样跳的:0->10->20->30->40->50,先跳到50再说,因为0到50这一段相当于没用的,只有50到55这一段才会发生DP的状态转移。
因此:对于距离大于t的石子,可以把距离s对t取模,也就是(s%t)+t(+t是因为两个点不能重复,取模(s%t)有可能变成0)。
经过这样处理以后,这个距离就短多了,因为t最大就是10,m是100,撑死了也就是100*10=1000。
最后记得要多跑一点,因为只要大于等于l,所以转移要转移到l+t-1那个位置。(为了贪方便我就直接+100了)。
下面是代码:
#include<bits/stdc++.h> #define fs first #define se second #define pb push_back #define cppio ios::sync_with_stdio(false);cin.tie(0) using namespace std; typedef long long ll; typedef pair<int,int> pii; typedef vector<int> VI; const int maxn=1e6+6; const ll inf=0x3f3f3f3f; const ll mod=1e9+7; int dp[maxn],a[maxn],vis[maxn]; int main(){ int l; scanf("%d",&l); int s,t,m; scanf("%d%d%d",&s,&t,&m); for(int i=1;i<=m;i++){ scanf("%d",a+i); } sort(a+1,a+1+m); a[0]=0;a[m+1]=l;int tmp=0; for(int i=1;i<=m+1;i++){ if(a[i]-a[i-1]>t){ tmp+=(a[i]-a[i-1])%t+t; } else tmp+=a[i]-a[i-1]; vis[tmp]=1; } memset(dp,0x3f,sizeof(dp)); dp[0]=0; for(int i=1;i<=tmp+100;i++){ for(int j=s;j<=t;j++){ if(i>=j) dp[i]=min(dp[i],dp[i-j]+vis[i]); } } int ans=inf; for(int i=tmp;i<=tmp+100;i++){ ans=min(dp[i],ans); } printf("%d",ans); return 0; }