30分做法:
dp[i]表示青蛙跳到位置i经过的最小石子数,从dp[i-j]转移过里,s<=j<=t。初始状态:dp[0]=0;
最后统计答案是从[n,n+t)区间中找出最小dp值。(n+t青蛙跳不到,所以开区间)
#include<queue> #include<cstring> #include<algorithm> #include<cstdio> #include<cmath> #include<iostream> using namespace std; int l,s,t,m; int a[107]; int b[11007]; int dp[11007]; int main() { scanf("%d%d%d%d",&l,&s,&t,&m); for(int i=1;i<=m;i++) { scanf("%d",a+i); b[a[i]]=1; } memset(dp,63,sizeof(dp)); dp[0]=0; for(int i=s;i<l+t;i++) { for(int j=s;j<=t;j++) { if(i-j>=0) dp[i]=min(dp[i],dp[i-j])+b[i]; } } int ans=99999; for(int i=l;i<l+t;i++) { ans=min(ans,dp[i]); } printf("%d ",ans); return 0; }
满分做法:
观察数据范围l<=1e9,这么大数组肯定存不下来,所以考虑路径压缩。因为s,t是1-10之间的数,所以无论青蛙怎么跳,他都能从当前位置i跳到i+2520*X的位置,
因为2520是1-10的最小公倍数,他的因子包含1-10所有数。对相邻两个石子的距离进行路径压缩,对2520取模(因为中间空余的长度大于2520,青蛙可以直接跳过,不会对答案有影响),
这样可以大大缩短桥的长度。重新更新石子的位置,最后桥长就等于最后石子的位置,其他操作和30分一样.
#include<queue> #include<cstring> #include<algorithm> #include<cstdio> #include<cmath> #include<iostream> using namespace std; const int maxm=1e6+7; int l,s,t,m; int a[117]; int b[maxm]; int d[117]; int dp[maxm]; int main() { scanf("%d%d%d%d",&l,&s,&t,&m); for(int i=1;i<=m;i++) { scanf("%d",a+i); } sort(a+1,a+m+1);//题目没说升序排列 for(int i=1;i<=m;i++)//路径压缩 { d[i]=(a[i]-a[i-1])%2520;//2520 1-10的最小公倍数 } for(int i=1;i<=m;i++) { a[i]=a[i-1]+d[i]; b[a[i]]=1; } l=a[m];//更新新长度 memset(dp,63,sizeof(dp)); dp[0]=0; for(int i=s;i<l+t;i++) { for(int j=s;j<=t;j++) { if(i-j>=0) dp[i]=min(dp[i],dp[i-j])+b[i]; } } int ans=99999; for(int i=l;i<l+t;i++) { ans=min(ans,dp[i]); } printf("%d ",ans); return 0; }