题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5248
分析:序列中元素的值不大于10^6,可知变换代价最多不会超过10^6,这样我们可以在这个范围内二分查找出最小的变换代价,初始化左右边界left=0,right=10^6+1,对于每一个代价mid=(left+right)/2,我们都遍历a数组(正序倒序遍历都行,区别就在于:正序遍历的话每个元素的值改变所需的代价是增加的,而倒序是减少的,我们拿倒序遍历来说),先定义tmp=a[n]+mid,然后从i=n-1开始往前遍历:
(1)如果遇到a[i]-mid>=tmp,说明当前元素a[i]减少mid(mid为当前情况的最大代价)时也不能满足题意中的“严格单调”情况,这时就说明mid的值选小了,令left=mid+1,然后重新遍历;
(2)如果a[i]+mid>tmp,说明当前元素a[i]在改变代价少于mid的情况下就可以满足“严格单调”的条件了,这时就更新tmp的值为a[i]+mid,因为我们要保证前面的每个值都小于后面的值,就需要找出后面元素中最小的一个元素,在其能改变的代价范围内确定前面元素的改变值;
(3)如果不满足上面两个条件,就说明当前的tmo值对于a[i]来说是合法的,为了保证“严格单调”,我们需要令tmp=tmp-1。
实现代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=100005;
int a[maxn],n;
//倒序遍历
bool judge(int mid)
{
int tmp=a[n]+mid;
for(int i=n-1;i>0;i--)
{
if(a[i]+mid<tmp)
tmp=a[i]+mid;
else if(a[i]-mid>=tmp)
return false;
else tmp--;
}
return true;
}
/*
//正序遍历
bool judge(int mid)
{
int tmp=a[1]-mid;
for(int i=2;i<=n;i++)
{
if(a[i]+mid<tmp+1) return false;
else if(a[i]-mid<tmp+1) tmp++;
else tmp=a[i]-mid;
}
return true;
}
*/
int main()
{
int t,T=1;
cin>>t;
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int l=0,r=maxn*10;
while(l<r)
{
int mid=(l+r)>>1;
if(judge(mid)) r=mid;
else l=mid+1;
}
printf("Case #%d:\n%d\n",T++,l);
}
return 0;
}