状态压缩的dp
状态转移方程
f(n)=min{f(n),f(n-i)}+(n位置有石头?1:0) S<=i<=T
对于30%的数据,L <= 10000;
对于全部的数据,L <= 10^9。
数据规模大,需要进行压缩
第一种情况:
当s=t时,很简单,青蛙踩到的石头肯定是s的倍数,那么只要统计一下所有石子中有多少是s的倍数,输出即可。
第二种情况:s<t
我们先来看一组数据。s=4,t=5。
从数据中我们看到,12以后的点全部都是可以到达的了。如果s=4,t=5,在一段100000的距离中没有石头,其实12以后的点都是不用递推就知道肯定能到达的。那么我们用原始的方法做就会浪费很大的资源。
所以当s=4,t=5时,如果一段没有石头的区间长度在4*5=20以外,那么我们只要递推前20就可以了,因为20后面的情况是一样的(仔细想想为什么?)。
假设s=3,t=5,那么11=3+4+4就也可以到达了。所以,只有当t=s+1时,连续的点的起始位置才能尽量后面。最坏情况就是s=9,t=10了(仔细想想为什么?)。
跟前面的s=4,t=5的情况一样,其实s=9,t=10时只要一段没有石头的区间长度在90之外,我们都把它当做90对待就可以了。
因此,我们每次对于一段没有石头的区间长度为x,如果x<=t(t-1),我们仍然把它当做x来处理;相反,当x>t(t-1)时,我们就把它当做t(t-1)处理。这样,最大的复杂度就是t(t-1)*(石头个数+1)=90*101=9090,比之前的复杂度大大降低。
import java.util.Arrays;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
goriver g=new goriver();
}
}
class goriver
{
private int L;
private int s,t,m;
private int[] stone;
private int[] dp;
public goriver()
{
Scanner cin=new Scanner(System.in);
L=cin.nextInt();
s=cin.nextInt();
t=cin.nextInt();
m=cin.nextInt();
stone=new int[m+2];
for(int i=1;i<=m;i++)
stone[i]=cin.nextInt();
stone[m+1]=L;
Arrays.sort(stone);
if(s==t)
{
int sum=0;
for(int i=1;i<=m;i++)
if(stone[i]%s==0)
sum++;
System.out.println(sum);
return ;
}
for(int i=1;i<=m+1;i++)
if(stone[i]-stone[i-1]>100)
{
int b=stone[i]-stone[i-1]-100;
for(int j=i;j<=m+1;j++)
stone[j]-=b;
}
L=stone[m+1];
int[] dp=new int[30000];
int[] isstone=new int[30000];
for(int i=1;i<=m;i++)
isstone[stone[i]]=1;
Arrays.fill(dp, 1, dp.length-1, Integer.MAX_VALUE>>1);
for(int i=1;i<=L;i++)
for(int j=i-t;j<=i-s;j++)
if(j>=0)
dp[i]=Math.min(dp[i], dp[j]+isstone[i]);
System.out.println(dp[L]);
}
}