斐波那契数列
1 1 2 3 5 8 13
F(N) = F(N-1) + F(N-2)
满足此类严格递归(无条件转移) 转换成O(logN)
斐波那契套路 复杂度O(logN)
1 1 2 3 5
[2 1]= [1 1] *
[F(N) F(N-1)] = [F(2) F(1)] *
设置 t =
res = E* * **
斐波那契公式 优化到O(logN)
public static int fi(int n){
if (n < 1){
return 0;
}
if(n == 1 || n == 2){
return 1;
}
int[][] base = { {1, 1}, {1, 0} };
int[][] res = matrixPower(base, n - 2);
return res[0][0] + res[1][0];
}
public static int[][] matrixPower(int[][] m, int p){
int[][] res = new int[m.length][m[0].length];
for (int i = 0; i < res.length; i++){
res[i][i] = 1;
}
int[][] t = m;
for(; p != 0; p >>=1) {
if ((p & 1) != 0){
res = muliMatrix(res, t);
}
t = muliMatrix(t, t);
}
return res;
}
public static int[][] muliMatrix(int[][] m1, int[][] m2){
int[][] res = new int[m1.length][m2[0].length];
for (int i = 0; i < m1.length; i++){
for(int j = 0; j < m2[0].length; j++){
for(int k = 0; k < m2.length; k++){
res[i][j] += m1[i][k] * m2[k][j];
}
}
}
}
推广
F(N) = 3F(N-1)-4F(N-3)+6F(N-5)
五阶矩阵
[F(N) ..... F(N-4)] = [F(5).....F(1)] *
题目:农场有一个牛,不会死,三年后产仔,问N年后有几只牛
A AB ABC ABCD ABCDEF ABCDEFGHI
1 2 3 4 6 9
F(N) = F(N-1) + F(N-3) 去年的加三年前的
三阶矩阵
题目:字符串只由0和1组成
当字符串长度为1时,所有可能的字符串为0和1
长度为2时,可能的字符串为00、01、10、11
如果某一个字符串中,只要出现0的位置,左边就靠着1,这样的字符串叫做达标字符串
给定一个正数N,返回所有长度为N的字符串中,达标字符串的数量
思路:i长度之前一定放着一个1字符
F(i) = F(i-1)+F(i-2)
题目二:n根木棍,第i根木棍的长度为i,像选出骑着三根组成三角形。最少去掉多少根木棍,使得任意的三根木棍都不能组成三角形
给定N = 17
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
斐波那契数列 1 1 2 3 5 8 13
去掉斐波那契数列外的木棍就是答案
证明:留下的数字abcdef 从小达大排序, 使得i位置的木棍 大于等于前两根相加 等于时候去掉的木棍最少
题目三:往背包里装一些零食,背包容量为w。家里一共有n袋零食,第i袋零食体积为v[i],想知道在总体积不超过背包容量的情况下,一共有多少种零食放法,总体积为0也算一种方法
假设背包容量为10,使用i位置的数和不用i位置的数
题目四:(IPO问题)有序表 先根据难度排序,由小到大,同样难度的工作在根据工作的报酬由大到小排。
保留每组的组长,在保留难度增加报酬也增加的工作,然后进行选择
public static class JobComparator implements Comparator<Job> {
public int compare(Job o1, Job o2){
return o1.hard != o2.hard ? (o1.hard - 02.hard) : (o2.money - o1.money);
}
}
public static int[] getMoneys(J0b[] job, int[] ability){
Arrays.sort(job, new JobComparator());
//难度为key的工作,最优钱数是多少,有序表
TreeMap<Integer, Integer> map = new TreeMap<>();
map.put(job[0].hard, job[0].money);
Job pre = job[0]; // pre之前组的组长
for(int i = 1; i < job.length; i++){
if (job[i].hard != pre.hard && job[i].money > pre.money) {
pre = job[i];
map.put(pre.hard, pre.money);
}
}
int[] ans = new int[ability.length];
for (int i = 0; i < ability.length; i++){
Integer key = map.floorKey(ability[i]);
ans[i] = key != null ? map.get(key) : 0;
}
return ans;
}
题目五:给定一个字符串,如果该字符串符合人们日常书写一个整数的形式,返回int类型的这个数;如果不符合或者越界返回-1或者报错
总结: 1)数字字符外只允许出现"-"
2)如果有-,只允许在开头出现一次,且后面必须跟着不为0的数字字符
3)如果开头第一个字符为0.后续必须没有数字
public static boolean isValid(char[] str){
if (syr[0] != '-' && (str[0] < '0' || str[0] > '9')){
return false;
}
// 1) str[0] == '-' && str.length == 1
// 2) str[0] == '-' && str.length != 1 && str[1] == '0'
if (str[0] == '-' && (str.length == 1 || str[1] == '0')){
return false;
}
if (str[0] == '0' && str.length > 1){
return false;
}
for (int i = 1; i < str.length; i++){
if (str[i] < '0' || str[i] > '9'){
return false;
}
}
return true;
}
判断的时候用负数接这个值,因为负数的范围比正数大
public static int convert(String s){
if(s == null || s.equals("")){
return 0; // can not convert
}
char[] str = s.toCharArray();
if (!isValid(str)){
throw new RuntimeException("can not convert");
}
boolean nef = str[0] == '-' ? true : false;
int minq = Integer.MIN_VALUE / 10;
int minr = Integer.MIN_VALUE % 10;
int res = 0;
int cur = 0;
for (int i =neg ? 1 : 0; i < str.length; i++){
cur = '0' - str[i];
//中途转化过程中,溢出的时候
if ((res < minq) || (res == minq && cur < minr)) {
throw new RuntimeException("can not convert");
}
res = res * 10 + cur;
}
//res是,转化数字绝对值的负数表达
if (!neg && res == Integer.MIN_VALUE){
throw new RuntimeException("can not convert");
}
return neg ? res : -res;
}