题目链接
题目大意
给出一个由 n 个数组成的数列 a,选择一个最小的 k ,代表可以进行 k 次操作,对于第 t 次操作可以选择任意个位置使得 a[ i ] = a[ i ] + 2^( t - 1 ),问最少需要多少次操作才能使序列满足非严格递增
题目思路
读完题后的第一反应是模拟+贪心,实际上不是的,一直wa,我的思路就是如果a[j]<premax,那么就要加(1<<i)一直wa
举一个反例 如
2 -1 -3 -4 第一步变为 2 0 -2 -3 第二步 2 2 0 -1 第三步 2 2 4 3 第四步 2 2 4 11那么答案就是4步,其实答案应该是3
2 -1 -3 -4第一步2 0 -3 -3 第二步 2 2 -1 -1 第三步 2 2 3 3
需要想到的一个知识点是,任何数字都可以用二进制来表示,相应的题目中的操作可以转换为二进制加法,进而转换为:对于某个位置,都可以加上任何一个数
因为需要让整个序列非严格递增,所以取任意两个位置 i 和 j 满足 i < j 讨论一下:
如果 a[ i ] <= a[ j ] :显然已经满足题目要求,无需操作
如果 a[ i ] > a[ j ] :让 a[ j ] 加上一个数变为 a[ i ] 显然是最优的
这样一来我们只需要维护一下前缀的最大值,对于后续的每个 a[ i ] ,找出最大的差值就好了
代码
#include<map>
#include<cstdio>
#include<cmath>
#include<vector>
#include<cstring>
#include<algorithm>
#define fi first
#define se second
#define debug printf("I am here\n");
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1e9+7;
int t,n,a[maxn];
int cal(int x){
int cnt=0;
while(x>0){//求x变为二进制中最大的那一位
x=x/2;
cnt++;
}
return cnt;
}
int main(){
scanf("%d",&t);
while(t--){
int premax=0,chamax=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
premax=a[1];
for(int i=2;i<=n;i++){
chamax=max(chamax,premax-a[i]);
premax=max(premax,a[i]);//前面的最大值
}
printf("%d\n",cal(chamax));
}
return 0;
}
参考链接:https://blog.csdn.net/qq_45458915/article/details/105485767?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase