剑指Offer刷题记录_Day23

数学 (medium)

Q1 I. 剪绳子

给你一根长度为 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。

方法一:数学方法

根据数学推论(能推导出来),将n划分成差不多大小的数组分段时乘积最大。根据对方程(n / x)^x 求导可以得出 x = e 时取极值。此时我们取 e 临近的2和3分别验证,可以发现3时更大。因此尽可能多划分3就能得到最大的乘积。

对三取模有三种情况0 1 2分别讨论:

class Solution {
public:
    int cuttingRope(int n) {

        if(n <= 3) return n-1;

        int m = n/3 ,r = n%3;
        
        if(r == 1) return pow(3,m-1)*4;
        else if(r == 2) return pow(3,m)*2;
        else return pow(3,m);

    }
};

更简洁的:

int cuttingRope(int n) {
    return n <= 3? n - 1 : pow(3, n / 3) * 4 / (4 - n % 3);
}

方法二:动态规划:注意dp数组前三个的意义,与 n > 3 不同。

class Solution {

public:
    int cuttingRope(int n) 
    {
        if(n <= 3) return n-1;
        vector<int> dp(n+1,1);

        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 3;

        for(int i=4; i<=n; i++)
          for(int j =2 ;j <= i/2 + 1 ; j++)
            {
                dp[i] = max(dp[i],dp[j]*dp[i-j]);
            }
        return dp[n];
    }

};

Q2 和为s的连续正数序列

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

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

  • 方法一:求和公式+遍历
class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {

        vector<vector<int>> res;

        for(int i = 1;i <= target/2 ; i++)
         {  for(int j = i+1; ;j++)
               {
                   if((i+j)*(j-i+1)/2 > target) break;
                   if((i+j)*(j-i+1)/2 == target)
                     {
                         vector<int> tmp;
                         for(int k=i;k<=j;k++)
                           tmp.push_back(k);
                         res.push_back(tmp);
                     }
                     
               }
          }
        return res;
    }
};
  • 方法二:滑动窗口(1. 本题用滑动窗口更简洁 2. 不用变量存储当前和 而直接用求和公式 代码会更简洁 )

    class Solution {
    public:
        vector<vector<int>> findContinuousSequence(int target) {
         
          if(target < 3) return {};
          int l = 1,r = 2;
          int tmp = 3;
         
          vector<vector<int>> res;
          if(target < 3) return res;
      
          while(r <= target/2 + 1)
          {
            if(tmp == target )
            { vector<int> t;
              for(int i=l;i <= r;i++)
                  t.push_back(i);
              res.push_back(t);
              r++;
              tmp += r;  }
            else if(tmp<target)
            {
              while(tmp<target)
              {
                r++;
                tmp += r;} }
            else{
              while(tmp > target)
              {
                tmp -= l;
                l++;} }
          }
          return res;
        }
    };
    
    

Q3 圆圈中最后剩下的数字

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

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

本题可以用基于 ArrayList 的模拟链表,但时间复杂度较高。

本题最优解是数学递推公式,但不容易一下子就想到。。。。

class Solution {
public:
    int lastRemaining(int n, int m) {
	  int p=0;
	  for(int i=2;i<=n;i++)
	  {
		  p=(p+m)%i;
	  }
	  return p+1;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值