算法:走N步

关注下方公众号,分享硬核知识

作者 | 小K

出品 | 公众号:小K算法 (ID:xiaok365)

01

故事起源

从原点出发,每次只能向东,向北,向西走,且不能走已经走过的地方,那么走N步共有多少种不同的方式呢?

02

分析

2.1

条件分析

首先分析题目的限制条件,其实就2个。一是只有3个方向,二是不能走回头路

2.2

走一步

走一步,只有3种路线。

2.3

走两步

走第二步要从前一步能到达的位置出发,所以总共就有7种方式。

2.4

规律

可以发现,每一步能走的地方其实与上一步有关。每次走一步,要保证不走回头路,也就是不走上一步走过的地方就行了。

第i-1步向东走,那么第i步只能向北、向东。

第i-1步向西走,那么第i步只能向北、向西。

第i-1步向北走,那么第i步可以向北,向东,向西。

03

算法建模

一维的f[i]只能记录一个总数,而不能记录状态,所以要再多一维记录每一步走的方向。


设f[i][0],f[i][1],f[i][2]分别表示:第i步向东、向西、向北走的不同路线总数。
则有如下递推关系:

  • 第i步向东,等于上一步向东和向北之和,即f[i][0] = f[i - 1][0] + f[i - 1][2]

  • 第i步向西,等于上一步向西和向北之和,即f[i][1] = f[i - 1][1] + f[i - 1][2]

  • 第i步向北,等于上一步向东,向西,向北之和,即f[i][2] = f[i - 1][0] + f[i - 1][1] + f[i - 1][2];

则走n步的总数即为f[n][0]+f[n][1]+f[n][2]。

代码实现

int f[100][3] = {0};
f[0][0] = 1;
f[0][1] = 1;
f[0][2] = 1;
for (int i = 1; i < n; ++i) {
    f[i][0] = f[i - 1][0] + f[i - 1][2];
    f[i][1] = f[i - 1][1] + f[i - 1][2];
    f[i][2] = f[i - 1][0] + f[i - 1][1] + f[i - 1][2];
}
cout << f[n - 1][0] + f[n - 1][1] + f[n - 1][2] << endl;

04

优化

设第i步的总方案数为s[i]。  

s[i]=f[i][0]+f[i][1]+f[i][2]。  

=2*f[i-1][0]+2*f[i-1][1]+3*f[i-1][2]。  

=2*s[i-1]+f[i-1][2]。  

而f[i-1][2]=f[i-2][0]+f[i-2][1]+f[i-2][2]=s[i-2]。  

得s[i]=2*s[i-1]+s[i-2]。

  

所以对公式变形,也可以通过一维完成递推。

代码实现

int s[100];
s[0] = 1;
s[1] = 3;
for (int i = 2; i < n; ++i) {
    s[i] = 2 * s[i - 1] + s[i - 2];
}
cout << s[n - 1] << endl;

05

总结

对于求总数的问题,递推的应用其实非常广泛,因为执行效率比较高。其它的方法比如枚举、搜索,效率就比较低了。递推算法也是非常考验抽象能力,很多问题最终的模型都很简单,但思考的过程却非常复杂,所以大家可以多思考一些递推问题,对于思维能力的训练有很大帮助。

本文原创作者:小K,一个思维独特的写手。
文章首发平台:微信公众号【小K算法】。

如果喜欢小K的文章,请点个关注,分享给更多的人,小K将持续更新,谢谢啦!

关注下方公众号,分享硬核知识

关注我,涨知识

原创不易,感谢分享

转发,点赞,在看

往期精彩回顾

我想要一个这样的电视柜

这一次,我想为外包正名

小姐姐让我帮忙修照片

分享给更多朋友,转发,点赞,在看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
磁盘调度算法是计算机操作系统中的重要算法之一,它可以优化磁盘上数据的读取和写入。其中,n扫描算法是一种常见的磁盘调度算法,它可以在一定程度上降低磁盘的寻道时间。 以下是一个简单的 Java 实现: ```java import java.util.ArrayList; import java.util.Collections; public class DiskScheduler { public static void main(String[] args) { int[] requests = { 176, 79, 34, 60, 92, 11, 41, 114 }; int head = 50; int n = 4; System.out.println("Original request order: "); for (int i = 0; i < requests.length; i++) { System.out.print(requests[i] + " "); } ArrayList<Integer> left = new ArrayList<Integer>(); ArrayList<Integer> right = new ArrayList<Integer>(); // 将请求按照距离磁头的距离分成左右两个部分 for (int i = 0; i < requests.length; i++) { if (requests[i] < head) { left.add(requests[i]); } else { right.add(requests[i]); } } // 对左右两个部分分别按照距离磁头的距离排序 Collections.sort(left); Collections.sort(right); int totalMovement = 0; int current = head; // 先扫描右边的请求 for (int i = 0; i < right.size(); i++) { int next = right.get(i); totalMovement += Math.abs(next - current); current = next; } // 如果左边有请求,需要反向扫描左边的请求 if (left.size() > 0) { totalMovement += Math.abs(current - 0); current = 0; // 从右向左扫描左边的请求 for (int i = right.size() - 1; i >= 0; i--) { int next = right.get(i); totalMovement += Math.abs(next - current); current = next; } // 扫描左边的请求 for (int i = 0; i < left.size(); i++) { int next = left.get(i); totalMovement += Math.abs(next - current); current = next; } } System.out.println("\nTotal head movement: " + totalMovement); } } ``` 以上代码中,我们以数组的形式定义了一组磁盘访问请求。接着,我们将请求按照距离磁头的距离分成左右两个部分,并对它们分别进行排序。然后,我们从右向左扫描右边的请求,再从左向右扫描左边的请求,计算出总的磁头移动距离。最后,我们输出磁头移动距离的结果。 注意,在实际应用中,我们需要根据不同的情况选择不同的磁盘调度算法,并且需要考虑到磁盘的实际特性,如磁盘的转速、缓存等等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值