题目描述
多多打算将N道试题按一定顺序排列,组成一场考试,每道题有一定的难度Ai,一场合理考试的题目难度应该是从低到高的,多多希望难度升序,但又不完全升序,相部的题目中前一道题比后一道题难一点点也是可以的。
在对于一个题目序列,只要相邻的题目中前一道题的难度不超过后一道题的难度+M,就认为是满足的序列。求有多个序列是满足的序列。
输入描述:
第一行输入为两个整数N,M,含义如题目描述所示。
第二行为N个整数,分别表示每道题的难度。
题目解析
题意
- 前 <= 后 + M
- 所有题目排序,难度从小到大
- 0~N-1上的卷子都用完,有多少合法的卷子
思路
先排序:
- 先将数组从小到大排序。为什么要排序呢?这样新出现的数就没有办法让不合法的卷子重新变合法了
- 假设从0到i之前有a套合法的卷子,新来了一个数x,看之前a套合法的卷子有了x的加入能整体增值多少套,不用关心之前不合法的卷子
问题是:怎么增值呢?
- x加到每一套合法的最后,前面的难度小,一定合法
- x往前插,要插到什么难度的前面呢?
- 因为:pre <= next + M
- x作为当前,往后面看,假设后一个位置是i,那么必须满足 x <= i + M,也就是说,i必须满足i >= X - M。
二分法
class Solution {
// arr[0..r]上返回>=t的数有几个, 二分的方法
// 找到 >=t 最左的位置a, 然后返回r - a + 1就是个数
int getCount(std::vector<int> &arr, int r, int t){
int L = 0, R = r, M = 0;
int ans = R + 1;
while (L <= R){
M = (L + R) / 2;
if(arr[M] >= t){
ans = M;
R = M - 1;
}else{
L = M + 1;
}
}
return R - ans + 1;
}
public:
// N道试题
// arr表示每道题的难度
// arr[i] <= arr[j] + M
// 求有多个序列是满足的序列。
int ways(std::vector<int> arr, int M){
int N = arr.size();
if(N == 0){
return 0;
}
std::sort(arr.begin(), arr.end());
int all = 1;
for (int i = 1; i < N; ++i) {
all = all * (getCount(arr, i - 1, arr[i] - M) + 1);
}
return all;
}
};
查之前>=某个数有多少个可以用树状数组优化
暴力递归
class Solution {
//pre上一次选择的卷子的难度索引
//idx当前正在做的决定
int process(std::vector<int> arr, int m, int index){
int N = arr.size();
if(index == m){
for (int i = 1; i < index; i++) {
if (arr[i - 1] > arr[i] + m) {
return 0;
}
}
return 1;
}
int ans = 0;
for (int i = index; i < N; ++i) {
swap(arr[index], arr[i]);
ans += process(arr, index + 1, m);
swap(arr[index], arr[i]);
}
return ans;
}
public:
// N道试题
// arr表示每道题的难度
// arr[i] <= arr[j] + M
// 求有多个序列是满足的序列。
int ways(std::vector<int> arr, int M){
int N = arr.size();
if(N == 0){
return 0;
}
return process(arr, 0, M);
}
};
类似题目