码迷,mamicode.com
首页 > 其他好文 > 详细

模拟过程的技巧

时间:2020-09-17 15:53:36      阅读:19      评论:0      收藏:0      [点我收藏+]

标签:多少   string   false   cin   获得   使用   长度   驱动   顺序   

算法题中有一种题型叫做模拟过程,这种算法题在面试中出现的频率非常之高,因为比较简单,代码量一般而言并不算长。因其并没有一套比较固定的框架,也能考验候选人的编程思维。当遇到一道算法题,发现其并不具备其他经典算法的特征时,可以尝试模拟过程的方向进行求解

方法

一般这种题目分为以下几个步骤进行求解:为什么要这么分步骤进行,因为这样可以使得循环的过程中某些变量的值进行处理的时候有一个明确的顺序而不会发生混淆(反正按照下面的模板进行肯定有好处,特别是处理当前层 和 转移 的顺序

模板

//初始化,
while(){ //for() 核心循环
	//处理当前层
    //转移变量发生转移
}

确定核心循环

一般模拟的问题都是考察使用for,while循环的熟练程度,甚至连数组都不会有所涉及,所以确定当前问题的一个核心循环十分关键,有些复杂的模拟过程问题,一旦找到了核心循环,对题目的理解就豁然开朗。有一个方向,思路会变得明朗。如果做到这个核心循环,这种没有固定框架模板可以套用的算法题将导致感觉无从下手。

  • 如果循环次数确定,而且每次循环中循环变量都固定发生变化(i ++ ,j ++)>使用for循环,如果循环次数不确定,或者循环变量不是每层(一次循环我将其称为层)都固定发生转移(比如不是每层都是i ++ ,j ++ ,i -- ,i += 2)>使用while循环
  • 在开始进行循环操作之前,转移变量和操作变量需要进行初始化一个起始的值。操作遍历初始化为尚未进行任何处理的值,转移变量初始化为第一层的值。

处理当前层

对操作变量进行操作处理,对当前层的逻辑进行操作,最难的地方就在于分清楚哪些变量是需要在当前层进行操作的。哪些变量是在转移层才进行操作的,各个变量处理的顺序是什么

转移变量转移至下一层

转移变量:就是主要用途是用于驱动循环,其初始化要初始化为刚进第一层的值,不知道怎么描述,只是个人的理解,大家可以直接理解为循环变量

转移变量转移到下一层其应该有的值,最简单的就是i ++ ,j ++这样的

注意转移可能有条件,不同条件下的转移方式可能不一样

例子

斐波拉契数列

菲波那契数列是指这样的数列: 数列的第一个和第二个数都为1,接下来每个数都等于前面2个数之和。给出一个正整数k,要求菲波那契数列中第k个数是多少。

【输入】

输入一行,包含一个正整数k。(1 ≤ k ≤ 46)

【输出】

输出一行,包含一个正整数,表示菲波那契数列中第k个数的大小。

【输入样例】

19
【输出样例】
4181
#include <iostream>
using namespace std;
int main(){
    int k;
    cin >> k;
    //初始化
    int a = 1,b = 1;//斐波拉契数列的前两层,这个是转移变量,所以初始化的时候,初始化为第一层的值,也就是数列的前两个项
    //核心循环
    for(int i = 3;i <= k;i ++){
        //当前层操作
      	int t = a + b;
        //转移变量发生转移,至下一层
        a = b;
        b = t;
    }
    cout << b;
    return 0;
}

134. 加油站

在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

说明:

如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。
示例 1:

输入:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]

输出: 3

解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int n = gas.length;
        for(int i = 0;i < n;i ++) {//枚举从每个加油站出发。看看能否符合条件
            int f = 0,t =i,s = 0;//初始化,其中t,f是转移变量,s是操作变量,f表示当前层走过的车站数,s表示当前层的汽油量
            boolean flag = false;
            //核心循环是刚好走过的车站书等于车站总数,说明饶了一圈
            while(f != n){
                if(t == n) t = 0;//对成环的处理,如果当前层的t已经走到了数组的最右边,则重置到0
                //对当前层进行逻辑操作
                s += gas[t];//对操作变量进行修改
                if(s - cost[t] < 0) break;
                else{
                    s -= cost[t];
                }
                //转移变量发生转移,转移给下一层
                f ++;
                t ++;
                
            }
            //出循环判断,只有两种可能一种是f = n 出循环 一种是break出循环,前者是找到了解,后者继续循环,不进行处理
            if(f == n) return i;
        }
        return -1;
    }
}

约瑟夫环问题的模拟解法

有N个人围成一圈,从第一个人开始报数,数到M的人就被枪毙,再有下一个人开始重新报数,数到M的那个人被枪毙,求最后活着的人是几号?

#include <iostream>
#include<cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

bool a[101];
int main(){
	int n,m;
	cin >> n >> m;
    //循环前进行初始化
	memset(a,0,sizeof a);//初始化数组,一开始全都是活着的人
	int f = 0,s = 0,t = 0;//f表示已经被枪毙的人的数量,s表示当前报数的是什么,t表示当前报数的人的编号。
	while(f != n - 1) {//被枪毙的人的数目达到了n - 1个的时候,核心循环
        //当前层的操作
		if(t == n) t = 0;//因为是一个环,所以最后一个与第一个相连。
		if(!a[t]) s ++;//如果当前这个编号的人活着 就会报数 就会s ++
		if(s == m) {//当有人报数到M的时候,这个人被枪毙
			s = 0;//又重新开始报数,计数器归0
			cout << t << " ";//输出这个被枪毙的 人的编号
			a[t] = true;//标记为被枪毙
            //转移变量进行转移,转移给下一层
			f ++;//被枪毙的人数+ 1
		}
        //转移变量进行转移,转移给下一层
		t ++;//下一个人继续报数,
 	}
	return 0;
}

关于约瑟夫环的递归解法可以看我的博客

金币

国王将金币作为工资,发放给忠诚的骑士。第1天,骑士收到一枚金币;之后两天(第2天和第3天)里,每天收到两枚金币;之后三天(第4、5、6天)里,每天收到三枚金币;之后四天(第7、8、9、10天)里,每天收到四枚金币……这种工资发放模式会一直这样延续下去:当连续 n 天每天收到 n 枚金币后,骑士会在之后的连续n+1天里,每天收到n+1枚金币(n为任意正整数)。

你需要编写一个程序,确定从第一天开始的给定天数内,骑士一共获得了多少金币。

【输入】

一个整数(范围1到10000),表示天数

【输出】

骑士获得的金币数。

【输入样例】

6

【输出样例】

14

核心循环很好确定,就是遍历每一天

#include <iostream>
using namespace std;
int main(){
	int day;
	cin >> day;
	int sum = 0;//操作变量
	int now_gold = 1,now_day = 0;//当前每天发的金币数,转移变量 
	for(int i = 1;i <= day;i ++) {
        //对当前层的处理
		sum += now_gold;
        //转移变量发生转移,至下一层
		now_day ++;//领当前金币数的天数又多了一天
		if(now_gold == now_day) {//now_day now_gold的转移是有条件的,如果领当前金币数的天数已经等于金币数了,就发生如下转移。
			now_day = 0;
			now_gold ++;
		}
	}
	cout << sum;
	return 0;
}

还有很多这种模拟过程的题目,接下来几天还会继续补充~

模拟过程的技巧

标签:多少   string   false   cin   获得   使用   长度   驱动   顺序   

原文地址:https://www.cnblogs.com/yizhanwillsucceed/p/13620377.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!