题目一
遍历数组,统计三种数:不含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;
}