输出只包括一个整数,表示青蛙过河最少需要踩到的石子数。
解析:本题目是自己碰到的第一个动态规划题目,遇到的难度也是前所未有。自己只能窘迫的说水平太低了,连续提交了十几遍才通过,这还是在参考别人代码的基础上AC的。顺别吐槽一下,Vijos的系统也实在太差了,自动删除回车换行打乱格式导致编译通不过,最后还逼着我换了firefox才可以顺利提交。
思路一:也是自己最开始的思路,我想到了递归求解此问题。虽然对于样例很容易的通过,但是提交之后10组测试数据,没有一对是正确的,各种窘迫,考虑到数据规模还以为是自己的数据类型定义的太小,还专门换成了long类型,但是提交之后仍然出错,这是关注错误信息,发现给出的是堆栈溢出,这时候就是傻子也会想到应该是递归层次太多导致的,看到那个L的范围,溢出也是理所应当的。不得不放弃这种思路,但是自己有不会别的思路,只能参考网友上各种牛们提供的代码。下面是自己的递归代码,还是后者脸皮贴出来吧。
#include
#include
#include
int NumStone = 101;
int IsStone(int destinaton,unsigned int Stone[],int M)
{
int high = M -1;
int low = 0;
while(low <= high)
{
int mid = (low + high) / 2;
if(destinaton == Stone[mid])
return mid+1;
if (destinaton =0 && temp= L)
{
if(num < NumStone)
{
NumStone = num;
}
}
else
{
for(swap=S;swap<=T;swap++)
{
if (IsStone(location,Stone,M))
{
num++;
}
cal(location+swap,num,L,S,T,Stone,M);
}
}
}
int main()
{
unsigned int L;
int S,T,M,Temp,n;
scanf("%ld",&L);
scanf("%d %d %d",&S,&T,&M);
unsigned int *Stone = (unsigned int *)malloc(sizeof(unsigned int)*M);
for (n=0;n
思路二:这是人家正统的思想分析,我就直接贴过来吧,代码也是根据理解写的。方法为动态规划+路径压缩
本题是一类很有意思的动态规划题;不同于决策优化,本题要求优化状态,这就使题目增加了很多的灵活性。
朴素的动态规划注意到当前状态只与其前面的T个状态有关;所以说采用滚筒法可以在O(LT)的时间内解决本题。但是,本题中L非常大,因此我们希望算法的时间复杂度与L无关。
一种想法是:将当前状态s与其前面的T个状态看作一个长度为T+1的状态数组,如果在一次滚筒更新中新旧两个数组完全一样,则在遇到下一块石头之前,状态将会完全不变。这个原则是很简单的,因为除非遇到一块石头,否则每一个决策的前提都是不变的,所以说滚筒更新下去,状态一定不变。
那么,我们就需要问一个问题:究竟会不会出现这种更新前后状态不变的情况呢?如果不会出现这些情况,那么算法的优化也就无从谈起了。事实上,只要S < T,就一定会出现这种情况。
这是很好理解的。假设S < T,则青蛙可以跳T步或T-1步,这两个步长是互质的。根据扩展的欧几里德定理,当路程足够长的时候,一定会出现这样一种情况:前后T步全部被同一个数覆盖;这就可以直接应用优化了。
时间复杂度是O(NT)。 从桥的一侧到另一侧,中间最多只有100个石子。假设桥长为最大值(10^9),石头数也为最大值(100),则中间一定会有很多“空长条” (两个石子中的空地),关键是如何在处理时把这些“空长条”跳过,使得运算次数降到M次。
结论:
若(采用跳跃距离p和p+1时可以跳至任何位置Q),则在Q≥P*(P-1)时是一定有解的。
Because证明
由于p与p+1间隔1,故方程px+(p+1)y=Q有整数解,设其解为
x=x0+(p+1)t,y=y0-pt(t是整数)
取适当的t,使得0≤x≤p(只需在x0上加上或减去若干个p+1),则当Q>p(p-1)-1时,有
(p+1)y=Q-px>p(p-1)-1-px≥p(p-1)-1-p*p=-(p+1)
于是y>-1,故y≥0也是非负整数。证毕.
由于题目给出的一个区间是1≤S≤T≤10,于是当相邻的两个石子之间的距离不小于9*10=90时,则后面的距离都可以到达,我们就可以认为它们之间的距离就是90。如此一来,我们就将原题L的范围缩小为了100*90=9000,动态规划算法完全可以承受了。但是当S=T时,上述等式是无法使用的,在这种情况下,只需要在所有石子中,统计出坐标是S倍数的石子个数就可以了.
当然,如果S = T,这个性质显然就不成立了,这种情况下我们可以特判。
如果青蛙能够跳得步数不是连续的,这种优化还可以用吗?
可以的!如果青蛙跳得步数中有两个数是互质的,则优化立即生效;否则,我们将所有的石头的位置除以步数的最大公约数(不能被最大公约数整除的显然不可能被跳到),对总长度也做类似的变化,就可以套用优化方法了。
评价:这是NOIP中第一道DP优化题,虽然其难度远不如NOI,但其灵活度也不小,需要仔细地考察状态转移方程的特征,利用状态空间的稀疏性来进行优化。尤其是当S = T时这种特殊情况的讨论极易被遗漏。写代码的时候仍然暴露出很多问题,包括头文件的理解混乱,字符长度书写错误,边界问题考虑不周等。
下面是写出的代码:
#include
#include
int main()
{
long int L ,temp,k;
int S,T,M,i,j,min;
long stone[102],b[10000];
int Num[10000];
scanf("%ld",&L);
scanf("%d %d %d",&S,&T,&M);
for (i=0;istone[j+1])
{
temp = stone[j];
stone[j]=stone[j+1];
stone[j+1]=temp;
}
}
stone[M] = L;
if (stone[0]>90)
{
k = stone[0]-90;
for(i=0;i<=M;i++)
stone[i] -= k;
}
for (i=1;i<=M;i++)
{
if (stone[i]-stone[i-1]>90)
{
k = stone[i]-stone[i-1]-90;
for(j=i;j<=M;j++)
stone[j] -= k;
}
}
memset(Num,-1,sizeof(Num));
memset(b,0,sizeof(b));
//标记石头
for (i=0;i=0)
min = Num[j];
}
if (min != 101)
Num[i] = min+b[i];
}
min = 101;
for (i=stone[M];i
今天看到一个小规模的动态规划,取消了路径压缩的过程,作者的代码页比较好。
#include
#include
const int MAXN=100020;
int flag[MAXN];
int dp[MAXN];
int main()
{
int L,s,t,n;
int a;
while(scanf("%d%d%d%d",&L,&s,&t,&n)!=EOF)
{
memset(flag,0,sizeof(flag));
memset(dp,-1,sizeof(dp));//初始化,-1为不能到达的
//dp[i]表示到底 i 点需要经过的最少石子数,-1表示不能到达
for(int i=0;i=0&&dp[j]!=-1)//j 点能够跳到
{
if(dp[i]==-1)dp[i]=dp[j]+flag[i]; //第一次 直 接 给 值
else if(dp[i]>dp[j]+flag[i]) dp[i]=dp[j]+flag[i];//找小的值
}
}
}
int res=10000;
for(int i=L;i<=L+t-1;i++)//L 到 L+t-1 中最小的非 -1 值
{
if(dp[i]!=-1&&dp[i]