题目大意:
给定一个长度为n的序列,每一次可以选择一段连续的区间,将该区间每一个数都减1,每一个数的最小值为0,问最少需要执行多少次该操作。
虽然正解比较好像,但是我优化优化了暴力发现也过了。。。
朴素算法80分:
每一次从头找到第一个不为0的数,往后一直找到第一个0,他的前一个数如果是0,那么就代表可以结束了,如果不是,lr区间每一项就减去该区间最小值,并且ans++。
正确性还是比较显然的,不知道的话可以在下面留言。
随便搞搞100分:
由于n已经达到10的五次方,这种枚举方式很有可能会超时,我们考虑优化:
我们每一次记录一下找到的l,这个l的位置代表从头到这的数都是0,那么我们下一次枚举时从这里开始枚举就好了。
正解:
贪心策略:每当我们扫到一个数,如果它大于前一个数答案就加上差值,正确性?
这是比较显然的,如果比前一个数小,那么减前一个数时就可以顺便把他减掉,如果大于则还得自己减。
最后,附上本题代码:
随便搞搞:跑的还是挺快的,就比正解慢了4ms
1 #include<cstdio> 2 #define maxn 100000 3 using namespace std; 4 5 int n,a[maxn+5],ans,last; 6 7 int main() 8 { 9 scanf("%d",&n); 10 for(int i=1;i<=n;i++) 11 { 12 scanf("%d",&a[i]); 13 } 14 while(1) 15 { 16 int l=0,r=n,minn=10005; 17 for(int i=last;i<=n;i++) 18 { 19 if(a[i]>0) 20 { 21 l=i; 22 last=l; 23 break; 24 } 25 } 26 if(l==0) 27 { 28 break; 29 } 30 for(int i=l;i<=n;i++) 31 { 32 if(a[i]<=0) 33 { 34 r=i-1; 35 break; 36 } 37 if(a[i]<minn) 38 { 39 minn=a[i]; 40 } 41 } 42 for(int i=l;i<=r;i++) 43 { 44 a[i]-=minn; 45 } 46 ans+=minn; 47 } 48 printf("%d\n",ans); 49 return 0; 50 }
正解代码:
1 #include <iostream> 2 using namespace std; 3 int main() 4 { 5 int n,a,last=0,ans=0; 6 cin>>n; 7 for(int i=1;i<=n;i++) 8 { 9 cin>>a; 10 if(a>last) ans+=(a-last); 11 last=a; 12 } 13 cout<<ans<<endl; 14 }