剪绳子

medium 原题连接:剪绳子

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18

示例:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

  1. 2 <= n <= 58

解题思路

总长度固定,拆成不同长短的子段,算最大乘积

  1. 子段长度越相等,积越大(算术几何均值不等式)
  2. 不要分出长度为1的子段
  3. 自然对数 e 约等于 2.7

所以设总长度为常数N,拆成a段长度为x,我们有:

N = a * x ; ans = x ^ a;

得到:ans = ( x ^ ( 1 / x ) ) ^ N,由于N为常数,ans最终的值会趋近于 2.7 * N,但是绳子要是整数

所以目标是将绳子尽可能化成越多的3

  1. 当长度可以被3整除,全部转为长度为3的绳子,求值
  2. 当长度对3除余,得到2,转换为一个2和其他为3的数段,求值
  3. 当长度对3除余,得到1,转换为一个4和其他为3的数段,求值

Java代码

class Solution {
    public int cuttingRope(int n) {
        if(n <= 3)return n-1;
        int a = (int)n/3, b = n%3;
        if(b == 2) return (int)Math.pow(3,a)*2;
        if(b == 1) return (int)Math.pow(3,a-1)*4;
        return (int)Math.pow(3,a);
    }
}

和为s的连续正数序列

Easy 原题连接: 和为s的连续正数序列

输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。

序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

示例:

输入:target = 9
输出:[[2,3,4],[4,5]]
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]

限制:

  1. 1 <= target <= 10^5

解题思路

连续数组首先想到滑动窗口:

  1. 设立滑动窗口,左端left,右端right,得到窗口总值:sum = left + right
  2. 判断sum与target的关系:
    1. sum == target : 符合目标信息,存储滑动窗口中的数据,窗口小数端移动(符合题目要求:不同序列按照首数字大小排列)
    2. sum < target :滑动窗口大数端移动
    3. sum > target :滑动窗口小数端移动
  3. 循环2,直至left与right相遇:(窗口最小数已经大于target的一半,无法划分了)
// 当你需要从0开始赋值数组,但是你的值随另一个数组变动:
for(int i=left; i<=right; i++) {
    ans[i-left] = i;
}

Java代码

class Solution {
    public int[][] findContinuousSequence(int target) {
        int left = 1, right =2, sum = left + right;
        List<int[]> ansList = new ArrayList<>();
        while(left < right) {
            if(sum == target) {
                int[] ans = new int[right - left + 1];
                for(int i=left; i<=right; i++) {
                    ans[i-left] = i;
                }
                ansList.add(ans);
            }
            if(sum >= target) {
                sum -= left;
                left++;
            }else {
                right++;
                sum += right;
            }
        }
        return ansList.toArray(new int[0][]);
    }
}

圆圈中最后剩下的数字

Easy 原题连接:圆圈中最后剩下的数字

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

示例:

输入: n = 5, m = 3
输出: 3
输入: n = 10, m = 17
输出: 2

提示:

  1. 1 <= n <= 10^5
  2. 1 <= m <= 10^6

解题思路

约瑟夫环问题:每次都会删除环中一个节点,想知道谁留到最后,由题目提示给的数据量来看,死算是过不了的

首先,在最后一次删除后,所剩下的一个数,是当前数组的0位(因为只有一位了)

每次删除后,从下一个开始当作新的删除计数,删除 当前值+( m % i ), i 为当前循环中的数的总数,为了避免总数超出上线,对 i 去余

综合以上两者,可以从一位向前推到(已知最后剩下的是最后数组的0号位,能退出前一次删除的是哪个位置)

直到数组长度回复到n,返回最初0号位目前所在的位置即可

Java代码

class Solution {
    public int lastRemaining(int n, int m) {
        if(n == 1) return 0;
        int ans = 0;
        for(int i=1; i<=n; i++) {
            ans = (ans + m) % i;
        }
        return ans;
    }
}