[leetcode] 798 得分最高的最小轮调 - 思维dp
题目链接
轮调实际上是这个样子的:
每次讲最前面的元素放到数组最后,然后将所有元素集体向前移动一位
在当前值
a
[
i
]
≤
i
a[i] \leq i
a[i]≤i的时候会获得
1
1
1分,问最大的的分是多少?
先说明一个事实:
一次轮调之后,对于除了最前面的每个数,他的下标会减小
1
1
1,而对于最前面的那个数,他的下标直接变为最大
大致分为以下三种情况:
- 本来 a [ i ] a[i] a[i]就小于下标 i i i,轮调之后下标减小值不变,所以依旧会获得 1 1 1分
- 本来 a [ i ] = = i a[i] == i a[i]==i,轮调之后,下标减小而值不变,所以值就比下标大 1 1 1,所以说会失去 1 1 1分
- 本来 a [ i ] > i a[i] > i a[i]>i,轮调之后,下标更小,值依旧会大于下标,所以依旧不得分
- 在最前面的数,一次轮调之后,被放到最大的下标的位置,会得到 1 1 1分
我们用
d
p
[
i
]
dp[i]
dp[i]表示在第
i
i
i次轮调之后会得到的分数,那么就有:
d
p
[
i
]
=
d
p
[
i
−
1
]
−
x
+
1
dp[i] = dp[i-1] - x + 1
dp[i]=dp[i−1]−x+1
其中,
x
x
x表示
i
−
1
i-1
i−1次轮调时下标和值相等的个数{
在这里
x
x
x可以预处理得到
}
式中的
+
1
+ 1
+1是为了解决数组最前面的数到数组最后的最大下标处的贡献值
d
p
[
0
]
=
未
轮
调
的
时
候
的
得
分
dp[0] = 未轮调的时候的得分
dp[0]=未轮调的时候的得分
因为之和前一个关系有关,所以我们可以只用一个变量解决,记录最大值处的下标返回即可
Code:
class Solution {
public:
int bestRotation(vector<int>& nums) {
int val = 0;
const int n = nums.size();
int a[n+1],pos = 0;
memset(a,0,sizeof a);
for(int i = 0; i < n;i ++) {
if(nums[i] <= i) val ++;
}
int mxval = val;
for(int i = 0; i < n;i ++) {
if(i >= nums[i]) a[i-nums[i]] ++;
else a[i + n - nums[i]] ++;
}
for(int i=1;i<n;i++) {
val = val - a[i-1] + 1;
if(val > mxval) {
val = mxval;
pos = i;
}
}
return pos;
}
};