题目描述
给定一个长度为 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;
}