Mz要品尝美食,厨师给Mz准备了依次N道菜,其中第i道菜的美味度为正整数Ai。这时,Mz表明他希望这N道菜的美味度递增,厨师不能改变菜的顺序,只能修改一些菜的美味度使得Mz满意,修改后的美味度仍应该是正整数。厨师想知道他至少要修改几道菜。
第一行,一个正整数N。
第二行,N个正整数Ai。
仅一行,一个整数,表示最少需要修改的菜的数量。
4
1 3 2 4
2
对于50%的数据,N<=1,000。
对于100%的数据,N<=100,000,Ai<=1,000,000,000。
最后得知方法是这样的:
(以下引用自题解,大概来自黄学长hzwer,黄学长博客链接见博客首页左侧友链栏)
题目说严格上升,也就是对每个 i<j 都必须 a[i]-i <=a[j]-j
下面证明这个式子的正确性:
严格单调递增序列有 a[i+1]-a[i] > 0
由于是整数,所以 a[i+1]-a[i] >= 1
稍微变形也就是 a[i]-i <= a[i+1]-(i+1)
于是由不等式的传递性 a[i]-i <= a[i+1]-(i+1) <= ..... <=a[j]-j
于是我们令b[i]=a[i]-i,则a[i]的严格递增等价于b[i]的单调不降
所以只需要求出b[i]的最长不下降子序列,把不在序列中的那些数b[i]都改成符合条件的数(比如说和左边最近一个在最长不下降子序列中的b[j]相等)就能满足题意了
当然,我们并不需要求出具体的修改方案,我们只需要求出最长不下降的长度K,输出N-K即可
这样就是读入的时候减去序号,然后做一个最长不降子序列就可以了。
但是,这可是O(n²)的算法啊。如果遇到大数据呢?
注意,我们只需要求出最长不下降的长度K,因此我们可以用一种更为简单的方法。
这种方法只能用于求长度,不能输出最长上升子序列。二分查找复杂度为对数,扫一遍O(n),综合复杂度O(nlogn)
我们来看这样一组数据:
1 2 3 40 50 9 11 18 60
用一个指针来表示当前最长不降子序列长度
对于这样一个序列,我们的处理方式是:
首先1进入数组,指针+1;
2比1大,进入数组,指针+1;
以此类推,3 40 50 进入数组 当前数组:1 2 3 40 50 指针为5
接下来是9,比50小,那么我们从当前数组中从前往后找到一个比他大的最小的数,也就是40,然后把40换成9
注意,这时指针仍然为5 也就是说,最长不降子序列长度不变
同理,50被11替换;
之后,18 60分别接到后面,最后指针为7,也就是最长不降子序列长度
基本策略:不降则加在后面,降则替换。
你可能会问:如果50后面只有9,那不就成了 1 2 3 9 50了吗?9怎么出现在50前面了?
非也。替换并非真的替换。如果50后面只有9,那么50并不会被替换,9代表了40,表示50前一定有一个数能够在这里构成最长不降子序列。也就是说,现在的序列其实应该是1 2 3 40 50,但在数组中表现为1 2 3 9 50
这就是为什么我们说替换不会影响当前最长不降子序列长度。
(貌似)可以用这种方法做的题目:4214 2188 3955
至于具体二分的实现,我们这里要找的比当前x大的最小的f[k]
二分的不同姿势,大致是:如果要找最小就往左靠,找最大就往右靠。
具体来说就是:
查找满足a[x]>=k条件的最小的x
mid=(L+R) div 2;
if a[mid]>=k
then r:=mid
else l:=mid+1;
查找满足a[x]<=k条件的最大的x
mid:=(l+r+1) div 2;
if a[mid]<=k
then l:=mid
else r:=mid-1;
所以最后放上代码吧:
//codevs4214 Æ·³¢ÃÀʳ
//³ö×ÔTY¾ý20151011Ä£ÄâÈü
//copyright by ametake
#include
#include
#include
#include
using namespace std;
const int maxn=100000+10;
int n,a[maxn];
int f[maxn];
int p;
int find(int x)//Ä¿±ê£ºÕÒ³öµ±Ç°ÐòÁÐÖбÈx´óµÄµÚÒ»¸öÊýµÄλÖÃ
{
int l=1,r=p;
while (l
=x) r=mid; else l=mid+1; } return l; } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%d",&a[i]); a[i]-=i; } p=1;//pointer f[1]=a[1]; for (int i=2;i<=n;i++) { if (a[i]>=f[p]) { f[++p]=a[i]; } else { int pos=find(a[i]); f[pos]=a[i]; } } printf("%d",n-p); return 0; }
————————————————————————
闭关第二天,又长见识不少···继被差分和LCA虐过之后,今天又被DP,状压,二分和广搜虐,甚至被暴力虐TUT
然而···有匪君子,如切如磋,如琢如磨。罗马非一天建成,他需要每一天每一年持久不懈的努力,OI之路也是如此,一分辛苦一分田。
——明月别枝惊鹊,清风半夜鸣蝉