第五节课:斐波那契套路


题目一

在这里插入图片描述
遍历数组,统计三种数:不含2因子的数(奇数)a个,只有一个2因子的数b个,有两个及以上2因子的数c个;b=0–>奇4奇4…–>a=1,c>=1,a>1,c>=a-1;b!=0–>b个数连续放+4奇4奇…–>a=0,c>=0,a==1,c>=1,a>1,c>=a–>c>=a

在这里插入图片描述
代码实现:

bool f(vector<int>& arr) {
	if (arr.size() < 2) {
		return false;//不存在相邻的两个数,无效输入
	}
	int a = 0;
	int b = 0;
	int c = 0;
	
	for (int i = 0; i < arr.size(); i++) {
		if (arr[i] % 2 == 1) {
			a++;
		}
		else {
			if ((arr[i] / 2) % 2 == 1) {
				b++;
			}
			else {
				c++;
			}
		}
	}
	bool flag = false;
	if (b == 0) {
		if (a == 0||a==1||(a > 1 && c >= a - 1)) {//至少有两个数
			flag = true;
		}
	}
	else {
		if (c >= a) {
			flag = true;
		}
	}
	return flag;
}

斐波那契套路

除了初始项之外,后序每一项都有严格递归式的问题,时间复杂度O(logN)。注意是严格递归式,不能有条件选择。
斐波那契数列问题(二阶):F(N)=F(N-1)+F(N-2)

在这里插入图片描述
根据初始项可以求出系数矩阵,进而将原递归问题变成一个矩阵求幂的问题,而矩阵求幂有O(logN)的方法
在这里插入图片描述
先看一个数如何快速求幂ab:将十进制的幂变成二进制的幂;初始化结果res=1,初始化变量t=a1;以后每次t=tt,得到t^m,如果m的对应的二进制数为1的位在b中也为1,则将t乘到res中,即res=t,否则不乘。由于t是成倍增加的,因此时间复杂度为O(logN)。

在这里插入图片描述
矩阵的快速幂同理:

在这里插入图片描述
推广–>斐波那契套路:首先判断是几阶的递推关系,决定是几阶的系数矩阵;根据初始项求出系数矩阵;递推出F(N)与初始项的关系;根据上述求法求系数矩阵的幂。例如五阶的递推关系:
在这里插入图片描述
代码实现:

//O(1)
vector<vector<int>> multiMatrix(vector<vector<int>>& a, vector<vector<int>>& b) {
	int M = a.size();
	int N = a[0].size();
	vector<vector<int>>res(M, vector<int>(M));
	for (int i = 0; i < M; i++) {
		for (int j = 0; j < M; j++) {
			for (int k = 0; k < N; k++) {
				res[i][j] += a[i][k] * b[k][j];
			}
		}
	}
	return res;
}

vector<vector<int>> matrixPower(vector<vector<int>> base, int n) {
	vector<vector<int>>res(base.size(), vector<int>(base[0].size()));
	for (int i = 0; i < base.size(); i++) {
		res[i][i] = 1;
	}
	for (; n != 0; n=(n >> 1)) {
		if ((n & 1) == 1) {
			res = multiMatrix(res, base);
		}
		base = multiMatrix(base, base);
	}
	return res;
}

int fi(int n) {
	if (n < 1) {
		return 0;
	}
	if (n == 1 || n == 2) {
		return 1;
	}
	vector<vector<int>>base({ {1,1},{1,0} });
	vector<vector<int>>res = matrixPower(base, n - 2);

	return res[0][0] + res[1][0];
}


题目二

有一只母牛,每年生一只母牛,三年后新生的母牛成熟了,一块生母牛,并且假设,牛不会死,问n年后母牛的数量?
在这里插入图片描述
代码实现:

vector<vector<int>>multiMatrix(vector<vector<int>>& a, vector<vector<int>>& b) {
	int M = a.size();
	int N = a[0].size();
	vector<vector<int>>res(M, vector<int>(M));
	for (int i = 0; i < M; i++) {
		for (int j = 0; j < M; j++) {
			for (int k = 0; k < N; k++) {
				res[i][j] += a[i][k] * b[k][j];
			}
		}
	}
	return res;
}

vector<vector<int>> matrixPower(vector<vector<int>>base, int n) {
	int N = base.size();
	vector<vector<int>>res(N, vector<int>(N));
	for (int i = 0; i < N; i++) {
		res[i][i] = 1;
	}
	for (; n != 0; n = (n >> 1)) {
		if ((n & 1) == 1) {
			res = multiMatrix(res, base);
		}
		base = multiMatrix(base, base);
	}
	return res;
}

int f(int n) {
	if (n < 1) {
		return 0;
	}
	if (n < 4) {
		return n;
	}
	vector<vector<int>>base(3, vector<int>(3));
	base[0][0] = 1;
	base[0][1] = 1;
	base[1][2] = 1;
	base[2][0] = 1;
	vector<vector<int>>res = matrixPower(base, n - 3);
	return 3 * res[0][0] + 2 * res[1][0] + res[2][0];
}


题目三

一只母兔,每年生两只母兔,新生的兔子两年之后成熟,会一块生母兔,兔子会在五年之后死掉,求n年后的兔子数量?
在这里插入图片描述


题目四

在这里插入图片描述
方法一:打表
方法二:F(i):长度为i的字符串,每个位置都可以填0/1有多少是达标的。要想达标第一个字符必须是1,则F(i)的实际长度为i+1,只是第一个位置确定为1了

在这里插入图片描述
在这里插入图片描述


题目五

在这里插入图片描述
将保留的木棍按照长度从小到大排列,想要任意三根都不能组成三角形,则当前位置的木棍长度要大于等于其前两根木棍长度之和(不满足两边之和大于第三边的条件)。因为要求保留最多的木棍(去掉最少的木棍),将条件放宽为等于其前面两根木棍长度之和–>斐波那契数列–>即判断所给木棍长度序列中有多少属于斐波那契数列,这些为最多保留的木棍长度。
在这里插入图片描述


题目六

在这里插入图片描述

dp[i][j]:使用arr[i…]的零食组成体积为j的方法数,每一个位置可以选择用或者不用,最后将所有体积不超过总共体积的方法数相加。

在这里插入图片描述
代码实现:


//用v[i..]装满rest的方法数
int f(vector<int>& v, int i, int rest) {
	if (rest == 0) {
		return 1;
	}
	if (i == v.size() || rest < 0) {
		return 0;
	}
	return f(v, i + 1, rest - v[i]) + f(v, i + 1, rest);
}

//改动态规划
int process(vector<int>& v, int w) {
	vector<vector<int>>dp(v.size()+1, vector<int>(w + 1));
	for (int i = 0; i < dp.size(); i++) {
		dp[i][0] = 1;
	}
	
	for (int i = dp.size() - 2; i >= 0; i--) {
		for (int j = 1; j < dp[0].size(); j++) {
			dp[i][j] = dp[i + 1][j] + (j - v[i] >= 0 ? dp[i + 1][j - v[i]] : 0);
		}
	}
	int res = 0;
	for (int i = 0; i <= w; i++) {
		res += dp[0][i];
	}
	return res;	
}

//空间压缩
int process02(vector<int>& v, int w) {
	vector<int>dp(w + 1);
	dp[0] = 1;

	for (int i = v.size() - 1; i >= 0; i--) {
		for (int j = dp.size()-1; j >=1; j--) {
			dp[j] = dp[j] + (j - v[i] >= 0 ? dp[j - v[i]] : 0);
		}
	}
	int res = 0;
	for (int i = 0; i <= w; i++) {
		res += dp[i];
	}
	return res;
}

题目七

在这里插入图片描述
将工作按照难度从小到大排列,相同难度的工作按照报酬从大大小排;删掉难度相同,报酬低的工作;删掉难度增加,报酬每增加的工作;建立了难度递增,报酬也递增的单调性,每个人选择最接近自己工作能力的工作,所获得的报酬一定也最高的

在这里插入图片描述

class Job {
public:
	int money;
	int hard;
	Job(int money, int hard) {
		this->money = money;
		this->hard = hard;
	}
};

bool cmp(const Job& a, const Job& b) {
	if (a.hard < b.hard) {
		return true;
	}
	else if (a.hard == b.hard) {
		return a.money > b.money;
	}
	return false;
}

vector<int> getMoneys(vector<Job>& jobs, vector<int>& ability) {
	sort(jobs.begin(), jobs.end(), cmp);
	map<int, int>mp;
	mp.insert({ jobs[0].hard,jobs[0].money });
	Job pre = jobs[0];
	for (int i = 1; i < jobs.size(); i++) {
		if (jobs[i].hard != pre.hard && jobs[i].money > pre.money) {
			pre = jobs[i];
			mp.insert({ pre.hard, pre.money });
		}
	}
	vector<int>res(ability.size());
	for (int i = 0; i < res.size(); i++) {
		map<int, int>::iterator it = mp.lower_bound(ability[i]);
		if ( it!= mp.end()) {
			if (it->first == ability[i]) {
				res[i] = it->second;
			}
			else if (it != mp.begin()) {
				res[i] = (--it)->second;
			}
		}
	}
	return res;
}


题目八

在这里插入图片描述
总结:1) 除了数字字符之外,只允许有‘-’;2) 如果有‘-’,一定出现在开头,且后面一定有数字,且数字一定不是0;3) 如果开头是0,后序必无字符
不管原始的数字是正是负,全部按照负数来转化,因为负数的范围必正数的范围大一个,防止给定数字是最小的负数,最后在将符号考虑在内。
本题应格外注意,数字越界的问题,并且学习防止数字越界的技巧
代码实现:

//判断是否符合书写习惯
bool isValid(string& s) {
	if (s[0] != '-' && (s[0] > '9' || s[0] < '0')) {
		return false;
	}
	if (s[0] == '-' && (s.length() == 1 || s[1] == '0')) {
		return false;
	}
	if (s[0] == '0' && s.length() > 1) {
		return false;
	}
	for (int i = 1; i < s.length(); i++) {
		if (s[i] > '9' || s[i] < '0') {
			return false;
		}
	}
	return true;
}

int strToInt(string& s) {
	if (s.length() == 0) {
		return -1;//字符串为空
	}
	if (!isValid(s)) {
		return -1;//不符合书写习惯
	}
	bool neg = s[0] == '-' ? true : false;
	int minq = INT_MIN / 10;
	int minr = INT_MIN % 10;
	int res = 0;

	for (int i = neg ? 1 : 0; i < s.size(); i++) {
		int cur = '0' - s[i];
		if (res < minq || (res == minq && cur < minr)) {
			return -1;//越界
		}
		res = res * 10 + cur;
	}
	if (!neg && res == INT_MIN) {
		return -1;//越界
	}
	return neg ? res : -res;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值