递推式很容易想得到 f[i]=min(f[i-j]+当前点是否有石头,f[i])
直接DP +一些特判估计能骗50%吧。题目开的100%数据L为10^9,那以这样的转移方程肯定爆炸。
但是最后看了下题解发现还是用这个转移式,只不过要路径压缩(本蒟蒻不会,看了下题解里Panda_hu 大佬 终于懂了)
只要把相隔大于90的石头做一下路径压缩就行了,之后把总长度修改一下就OK了
为什么可以这样呢?
当S!=T时,任意步数p,p+1,肯定互质。。。然后gcd(p,p+1)=1,那么大于p*(p+1)的点在后面肯定也跑得了。那么就有下面代码。
#include <cstdio> #include <cmath> #include <cstring> #include <string> #include <cstdlib> #include <sstream> #include <iostream> #include <queue> #include <stack> #include <set> #include <map> #include <algorithm> #include <functional> using namespace std; #define ll long long #define re register #define fi first #define se second #define mp make_pair #define pb push_back #define P pair<int,int> void read(int &a) { a=0; int d=1; char ch; while(ch=getchar(),ch>'9'||ch<'0') if(ch=='-') d=-1; a=ch-'0'; while(ch=getchar(),ch>='0'&&ch<='9') a=a*10+ch-'0'; a*=d; } void write(int x) { if(x<0) putchar(45),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); } int f[10005],a[10005],b[10005],c[10005];///f[i]为在i位置踩石头最小的数 bool book[10005]; int main() { int L; read(L); int S,T,M; read(S); read(T); read(M); if(S==T) { int ans=0; for(re int i=1;i<=M;i++) { int x; read(x); if(x%S==0) ans++; } return write(ans),0; } for(re int i=1;i<=M;i++) read(a[i]); sort(a+1,a+M+1);///不排序会RE!!! memset(f,0x3f,sizeof(f)); a[0]=0,f[0]=0; c[M+1]=min(L-a[M],91);///最后一个石头与终点的最短距离,压缩 L=0; for(re int i=1;i<=M;i++)///压缩路径 b[i]=min(a[i]-a[i-1],90),L+=b[i],book[L]=1; L+=c[M+1]; for(re int i=S;i<=L+10;i++)///从S开始的原因是因为i<S时都跳过去了,f[i]肯定为0 for(re int j=S;j<=T;j++) f[i]=min(f[i],f[i-j]+(book[i]==1));///当前点与上一级跳过来的点+1做转移,这个转移方程应该很简单。 int ans=0x3fffffff; for(re int i=L;i<=L+10;i++) ans=min(f[i],ans); write(ans); return 0; }