剑指offer-算法和数据操作——动态规划、贪心算法与位运算

春招刷题笔记-剑指offer-算法和数据操作

  • 算法和数据操作——动态规划、贪心算法与位运算
    • 动态规划与贪心算法
      • 剪绳子
    • 位运算
      • 计算二进制中1的个数
      • 判断一个数是否是2的整数次方
      • 计算整数m的二进制需要改变多少位才能得到整数n的二进制

算法和数据操作——动态规划、贪心算法与位运算

动态规划与贪心算法

动态规划有以下几个特点:
一、如果是求一个问题的最优解,比如最大值或者最小值,并且问题可以分解为更小的重叠的子问题来解决;

二、整个问题的最优解是依赖于子问题的最优解的问题,一般使用动态规划来解决;

三、把大问题分解成若干个小问题,这些小问题之间还有相互重叠的更小的子问题;

四、由于子问题在分解大问题的过程中重复出现,为了避免重复计算子问题,我们可以用从下往上的顺序先计算出子问题的最优解并且保存下来,再以此为基础求取大问题的最优解。从上往下分析问题,从下往上求解问题。

剪绳子

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

//  附带上测试的程序
#include 
#include 
using namespace std;

// ===================动态规划=====================
int maxProductAfterCutting_solution1(int length) {
    //  动态规划就是以空间换取时间,将过程中可能会需要的中间
    //  数据全部计算出来并且保存下来,计算到最终的值就是解。
    if (length < 2) 
        return 0;
    if (length == 2)
        return 1;
    if (length == 3)
        return 2;

    int* maxpro = new int[length + 1];
    for (int i = 0; i < length + 1; i++)
        maxpro[i] = 0;
    
    //  这里的maxPro[2] = 2, 表明
    maxpro[0] = 0;
    maxpro[1] = 1;
    maxpro[2] = 2;
    maxpro[3] = 3;

    int max = 0;
    for (int i = 4; i <= length; i++) {
        max = 0;
        for (int j = 1; j <= i / 2; j++) {
            int temp = maxpro[j] * maxpro[i - j];
            if (max < temp)
                max = temp;
        }
        maxpro[i] = max;
    }

    int res = maxpro[length];

    delete[] maxpro;
    return res;
}

// ==================贪心算法=====================
int maxProductAfterCutting_solution2(int length) {
    if (length < 2) 
        return 0;
    if (length == 2)
        return 1;
    if (length == 3)
        return 2;
    
    int timesof3 = length / 3;

    if (length - 3 * timesof3 == 1)
        timesof3 -= 1;

    int timesof2 = (length - 3 * timesof3) / 2;

    return (int) (pow(3, timesof3)) * (int)(pow(2, timesof2));
}

// ====================测试代码====================
void test(const char* testName, int length, int expected)
{
    int result1 = maxProductAfterCutting_solution1(length);
    if(result1 == expected)
        std::cout << "Solution1 for " << testName << " passed." << std::endl;
    else {
        std::cout << "Solution1 for " << testName << " FAILED. ";
        cout << "length: " << length << " expected: " << expected << " result: " << result1 << std::endl;
    }

    int result2 = maxProductAfterCutting_solution2(length);
    if(result2 == expected)
        std::cout << "Solution2 for " << testName << " passed." << std::endl;
    else
        std::cout << "Solution2 for " << testName << " FAILED." << std::endl;
}

void test1()
{
    int length = 1;
    int expected = 0;
    test("test1", length, expected);
}

void test2()
{
    int length = 2;
    int expected = 1;
    test("test2", length, expected);
}

void test3()
{
    int length = 3;
    int expected = 2;
    test("test3", length, expected);
}

void test4()
{
    int length = 4;
    int expected = 4;
    test("test4", length, expected);
}

void test5()
{
    int length = 5;
    int expected = 6;
    test("test5", length, expected);
}

void test6()
{
    int length = 6;
    int expected = 9;
    test("test6", length, expected);
}

void test7()
{
    int length = 7;
    int expected = 12;
    test("test7", length, expected);
}

void test8()
{
    int length = 8;
    int expected = 18;
    test("test8", length, expected);
}

void test9()
{
    int length = 9;
    int expected = 27;
    test("test9", length, expected);
}

void test10()
{
    int length = 10;
    int expected = 36;
    test("test10", length, expected);
}

void test11()
{
    int length = 50;
    int expected = 86093442;
    test("test11", length, expected);
}

int main(int agrc, char* argv[])
{
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();
    test10();
    test11();

    return 0;
}

位运算

计算二进制中1的个数

题目描述:
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

class Solution {
public:
    int  NumberOf1(int n) {
        //  首先把N与1做位与的运算可以判定n的最后一位是不是1,接着把
        //  1向左移一位得到,再和n做位于运算,可以判定n的倒数第二位是不是1,
        //  接着再将2左移一位得到4,判定n的倒数第三位是不是1,依次计算下去,
        //  每一次的循环可以判数字的一个位是不是1。整数的二进制有多少位,
        //  就会进行多少次循环。
        int countof1 = 0;
        unsigned int flag = 1;
        while (flag) {
            if (n & flag)
                ++countof1;
            flag = flag << 1;
        }
        return countof1;
    }
};

//  一种比较惊喜的解决办法
class Solution {
public:
    int  NumberOf1(int n) {
        /*
        在数字的二进制表示中,如果把一个数减去1,那么,这个二进制数的
        最右边的1,会变成0,如果这个1后面有0,那么这个1后面的所有0都会
        编程1,1之前的数字保持不变。假如数字是12的二进制数1100,那么1100
        减去1会变成1011,,此时,如果把1100和1011做位与的运算1100&1011=1000
        相当于把1100的最右边的1变成了0,所以如果要计算二进制数中的1的个数,
        可以采用将数字减去1,并且和原数字做位与运算的方式来计算二进制中有多少
        个1.以下的部分是程序:
        */
       int countof1 = 0;
       while (n) {
           countof1++;
           n = (n - 1) & n;
       }
       return countof1;
    }
};

判断一个数是否是2的整数次方

bool PowerOf2(int n) {
    /*
    在数字的二进制表示中,如果把一个数减去1,那么,这个二进制数的
    最右边的1,会变成0,如果这个1后面有0,那么这个1后面的所有0都会
    编程1,1之前的数字保持不变。假如数字是12的二进制数1100,那么1100
    减去1会变成1011,,此时,如果把1100和1011做位与的运算1100&1011=1000
    相当于把1100的最右边的1变成了0,所以如果要计算二进制数中的1的个数,
    可以采用将数字减去1,并且和原数字做位与运算的方式来计算二进制中有多少
    个1.
    那么要计算一个数是不是2的整数次方,只需要判断这个数的二进制表示中是不是只有1个1
    也即如果只需要计算一次上述的运算,那么这个整数就是2的整数次方,如果需要进行1次以
    上的运算,那么这个整数就不是2的整数次方。
    */
    if (n <= 0) return false;
    n = (n - 1) & n;
    if (n == 0)
        return true;
    return false;
}

计算整数m的二进制需要改变多少位才能得到整数n的二进制

思路分析: 首先进行m与n的异或运算,再统计异或运算得到的1的个数,即为m的二进制改一定位数得到n的二进制。

int NumberNeededChannged(int m, int n) {
    int temp = m ^ n;
    int countof1 = 0;
    while (temp) {
        //  计算temp中1的个数
        ++countof1;
        temp = (temp - 1) & temp;
    }

    return countof1;
}

你可能感兴趣的:(春招刷题笔记)