dp+离散化
设dp[j]的含义是到位置j的陷进数,如果道路长度为1e9的话,数组是开不到这么大的,所以我们必须想办法缩短道路的长度,这就用到离散化了。
青蛙每次可以跳s-t之间的数,如果s==t的话,只需要枚举存储陷阱位置的数组,判断当前位置s是否可以走到即可。如果s!=t的话,我们来举个例子讨论下比如s=5 , t=6
跳一次 走到 5 - 6
跳两次 走到 10 - 12
跳三次 走到 15 - 18
跳四次 走到 20 - 24
跳五次 走到 25 - 30
跳六次 走到 30 - 36
跳七次 走到 35 - 42
跳八次 走到 40 - 48
跳九次 走到 45 - 54
跳十次 走到 50 - 60
我们可以发现在0 - st的位置中,并不是所有位置都可以走到,而在st以后的位置中都可以走到,既然这样,我们就可以把两点位置的距离>st的都变为st,这样的话,就缩短了道路的长度。举一个例子可能会理解的更明白一点
1e9
2 3 5
1 2 3 4 1e9
在这个例子里1e9>st,我们知道在大于st的位置,是一定可以走到的,我们与其去求到1e9的陷进数,倒不如去求到6的陷进数,因为他们两个的陷阱数是一样的,而且6又比1e9小,可以减少dp数组的空间。为什么说他们的陷阱数是一样呢?是因为他们之间没有其它陷阱,倒数第二个陷阱数在4这个位置,倒数第一个在1e9这个位置。但是如果数据是这样的 1 2 3 1e9 4 ,这样就不行了,因为在1e9 后面还有一个位于陷阱,这个陷阱的位置在1e9前面,如果我们还按照之前的方法算的话,计算到6的陷阱数,到6得以有到4和到3走到,但是我们还没有计算4这个位置的陷阱数,所以计算出来的结果是错误的(样例会卡这样的数据,所以我们直接对数组排序就可以了,这样就避免这样的情况发生)
到这里整体思路就讲完了,接下来说一下细节处理
//这是离散化的代码 for(int i=1;i<=m;i++){ int dis=arr[i]-arr[i-1]; if(dis>s*t)dis=s*t; now[i]=now[i-1]+dis; vis[now[i]]=1; }
说一下now这个数组的作用,因为离散化处理之后,位置都会发生改变,我们要开一个数组,记录下离散化处理之后,每个元素的位置,now[m]对应的就是arr中最后一个元素离散化的位置,如果桥的长度大于最后一个陷阱的位置加st那么到桥终点的方案数就等于到now[m]+st的位置的方案数,所以桥的长度l=now[m]+s*t
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; int arr[1110]; int vis[maxn]; int dp[maxn]; int now[110]; const int inf=0x3f3f3f3f; int main(){ int l; cin>>l; int s,t,m; cin>>s>>t>>m; for(int i=1;i<=m;i++){ cin>>arr[i]; } sort(arr+1,arr+1+m); if(s==t){ int count=0; for(int i=1;i<=m;i++){ if(arr[i]%s==0)count++; } cout<<count; return 0; } //now[0]=0; //memset(vis,0,sizeof(vis)); for(int i=1;i<=m;i++){ int dis=arr[i]-arr[i-1]; if(dis>s*t)dis=s*t; now[i]=now[i-1]+dis; vis[now[i]]=1; } l=now[m]+s*t; memset(dp,inf,sizeof(dp)); dp[0]=0; for(int i=1;i<=l;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=now[m];i<=l;i++){ ans=min(ans,dp[i]); } cout<<ans; return 0; }