一. 数组_数组的旋转_396. 旋转函数

题目描述
给定一个长度为 n 的整数数组 A 。

假设 Bk 是数组 A 顺时针旋转 k 个位置后的数组,我们定义 A 的“旋转函数” F 为:

F(k) = 0 * Bk[0] + 1 * Bk[1] + … + (n-1) * Bk[n-1]。

计算F(0), F(1), …, F(n-1)中的最大值。

注意:
可以认为 n 的值小于 105。

示例:

A = [4, 3, 2, 6]

F(0) = (0 * 4) + (1 * 3) + (2 * 2) + (3 * 6) = 0 + 3 + 4 + 18 = 25
F(1) = (0 * 6) + (1 * 4) + (2 * 3) + (3 * 2) = 0 + 4 + 6 + 6 = 16
F(2) = (0 * 2) + (1 * 6) + (2 * 4) + (3 * 3) = 0 + 6 + 8 + 9 = 23
F(3) = (0 * 3) + (1 * 2) + (2 * 6) + (3 * 4) = 0 + 2 + 12 + 12 = 26

所以 F(0), F(1), F(2), F(3) 中的最大值是 F(3) = 26 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-function

F(k) = 0 * Bk[0] + 1 * Bk[1] + … + (n-1) * Bk[n-1],要求计算F(0), F(1), …, F(n-1)最大值
最少不平移就是本身,最多平移n-1个位置,每次多平移一个就是向后移动一下
计算这些的最大值
这里一个是向后移动的函数,另一个是控制总共要移动多少次,每次移动后的F值计算
//使用这个函数会有时间超时

void rotate(vector<int>& nums, int k) {
    int n = nums.size();
    vector<int> temp(n);

    //这里创建一个数组,赋值为nums里面的值
    for(int i = 0; i < n; i++) {
        temp[i] = nums[i];
    }

    //这里的话,针对将对应temp数组里,按照平移后的位置赋值到nums对应的ind位置
    for(int i = 0; i < n; i++) {
        //取余是以右边的数n为准,右边的数控制范围
        int ind = (i + k) % n;
        nums[ind] = temp[i];
    }
}
//使用这个函数比上一个函数能稍微快一些,但也会超时
void reverser(vector<int>& nums, int i, int j) {
    while(i < j) {
        //swap(nums[start], nums[end]);
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
        i++;
        j--;
    }
}
void rotate(vector<int>& nums, int k) {
    int n = nums.size();
    //这里首先要对n取余,取余之后才能得到最小的移动步数
    k %= n;
    //得到步数之后,可以计算那些元素会移动到前面,那些元素会移动到后面
    reverser(nums, 0, n - 1);
    reverser(nums, 0, k - 1);
    reverser(nums, k, n - 1);
}
//每个位置向后移动一个位置,然后最后的一个元素去到第一个位置
//这是我能想到为当前应用适配的最快的平移算法
//首先保存最后的一个位置,从i-1到0开始,依次向后平移一个元素
//等所有的元素都平移完成之后,我们在将nums[0]填充为temp
//但就是这样,之后转成c运行或是c++运行,也不能通过,因为有一个特别长的测试用例
//这个只能每次向后平移一个,而且只适合当前的应用
void rotate(vector<int>& nums) {
    int n = nums.size();
    int temp = nums[n - 1];

    for(int i = n - 2; i >= 0; i--) {
        nums[i + 1] = nums[i];
    }

    nums[0] = temp;
}

int maxRotateFunction(vector<int>& nums) {
    int max_value = INT_MIN;
    int n = nums.size();

    //控制要移动多少次
    for(int i = 0; i < n; i++) {
        if(i != 0) {
            rotate(nums);
        }

        //每移动一个位置就要计算对应的值
        int value = 0;

        for(int j = 0; j < n; j++) {
            value += nums[j] * j;
        }

        //找到最大值。
        if(value > max_value) {
            max_value = value;
        }
    }

    return max_value;
}

这里是另一个思路
我通过观察发现
1 2 3 4 5
平移1次: 5 1234
平移2次: 45 123
平移3次: 345 12
平移4次: 2345 1
可以发现1,2,3,4,5元素作为第一个元素都出现了,那么我们可以遍历数组,遍历数组的所有位置
每个位置遍历n次,要是大于n-1的话,我们就取余,这样这些取最小的,就是结果。
但是还是不能通过,这是两层循环,在有一个测试用例中太长了,通不过。

int maxRotateFunction(vector<int>& nums) {
    int max_value = INT_MIN;
    int n = nums.size();

    for(int i = 0; i < n; i++) {
        int value = 0;

        for(int j = 0; j < n; j++) {
            value += nums[(i + j) % n] * j;
        }

        if(value > max_value) {
            max_value = value;
        }
    }

    return max_value;
}

目前尝试的这些想法都是不行的,只因为二层for循环,会浪费很多时间
动态规划思想
F(0) = 0a + b1+c2+d3
F(1) = 0d + a1+b2+c3
F(2) = 0c + d1 + a2+ b3
F(3) = 0b + c1 + d2 + a3
F(1)比F(0)多了a+b+c,少了3d
F(2)比F(1)多了d+a+b少了3c
这里一共四个F,每次少的为d,c,b分别可以得到F1,2,3
这里找到一个规律dp[i] = dp[i-1] + (sum - nums[i]) - (n-1)*nums[i]

    int n = nums.size();
    int sum = 0;
    vector<int> dp(n);

    //这里计算sum,和初始dp0
    //sum为dp[0]+...+dp[n-1]的和
    //这里计算dp[0]
    for(int i = 0; i < n; i++) {
        sum += nums[i];
        dp[0] += nums[i] * i;
    }

    //我们后面的话根据dp继续计算dp1~n-1
    int ind = n - 1;
    int i = 1;

    //将剩余的dp位置的值计算出来
    while(i < n) {
        dp[i] = dp[i - 1] + (sum - nums[ind]) - (n - 1) * nums[ind];
        ind--;
        i++;
    }

    //取得dp中的最大值
    return *max_element(dp.begin(), dp.end());
}
int main() {
    vector<int> nums = { 1, 2, 3, 4, 5 };
    maxRotateFunction(nums);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值