水平方向上有n个格子,格子上写着1到n的数字中的一个,游戏规则是:走到某一格,上面的数字假如是s,则可以选择跳到第s个格子或者前进一格,问你最少要走多少步才能到达最后一格并且返回起点。如:3 5 3 2 6 5 ,你去的时候可以一步一步到终点(5步),也可以一开始就跳到第三格,再一步一步到终点(4步),或者先前进一步再跳到第5格再一步一步地走(3步),最少是3步到达。然后要再返回的时候,最少步数的走法是,左移两步再跳到第二格再左移一步(4步),共7步。
分析:刚开始做的时候没有头绪,用的基本等于猜的方法,比如从题中第一格开始,到第三格,然后返回来查是否还有大的数字(比如5),这种方法最后加了很多复杂的约束,最终也没能完美的做出这道题,在参考了网上的方法之后,发现自己太小白了以及数学真是牛逼的学科!对于此类题用的是数学归纳法证明操作到f[n]是1到n的最小步数。
1.从第一步到第一步也就是f[1] = 0满足条件;
2.假设从1到k步都满足f[k]是最小步数;
3.假设1到n存在一个最优路径,而它的上一个格子是k,那么f[n] = f[k] + 1,否则从1到k一定可以走出比f[k]更小的最优路径,而这是不可能的。代码如下:
1 #include "stdafx.h" 2 #include <stdlib.h> 3 const int Max = 100001; 4 int count[Max] = {0}; 5 //用指针传入数组~真是好用就是还不知道风险,这个方法是计算出走过去需要多少步 6 int Come(int n,int *a) 7 { 8 //初始化第一步到第一步需要0步. 9 count[1] = 0; 10 int k; 11 //其他格子赋初值最大,这样在后面容易进行比较 12 for(int s = 2;s <= n;s++) 13 count[s] = Max; 14 //由于每一次具有两个选择,向前或者跳到相应格子所以,从第一步开始计算。 15 for(k = 1;k < n;k++) 16 { 17 //本格子的下一个格子需要多少步,比较count[k+1]和count[k] + 1取小值 18 if(count[k + 1] > count[k] + 1) 19 count[k +1] = count[k] + 1; 20 //本格子跳到的相应格子需要多少步,比较count[a[k]]和count[k] + 1取小值 21 if(a[k] > k && count[a[k]] > count[k] + 1) 22 count[a[k]] = count[k] + 1; 23 } 24 return count[n]; 25 } 26 //同上 27 int Back(int n,int *a) 28 { 29 count[n] = 0; 30 int k; 31 for(int s = n -1;s >= 1;s--) 32 count[s] = Max; 33 for(k = n;k > 1;k--) 34 { 35 if(count[k - 1] > count[k] + 1) 36 count[k -1] = count[k] + 1; 37 if(a[k] < k) 38 count[a[k]] = count[k] + 1; 39 } 40 return count[1]; 41 } 42 int _tmain(int argc, _TCHAR* argv[]) 43 { 44 int n; 45 //C语言不支持动态数组 46 int cell[Max] = {0}; 47 while(scanf("%d",&n) != 0) 48 { 49 printf("请输入格子数: "); 50 scanf("%d",&n); 51 printf("请输入%d个数: ",n); 52 for(int k = 1;k <= n;k++) 53 { 54 scanf("%d",&cell[k]); 55 } 56 printf("过去的步骤一共 %d 步 ",Come(n,cell)); 57 printf("回来的步骤一共是 %d 步 ",Back(n,cell)); 58 } 59 system("Pause"); 60 return 0; 61 }
数学真的是一个牛逼的学科,想学好算法没有数学基础真的要走很多弯路,而且写出来的还不是好程序...在被虐的路上小白在进步。