CCF NOIP2014前的复习(9.18~9.19)

2014.9.18

  今天决定开始复习基本数据结构,这部分貌似不是很难,去年赛day2第一题《积木大赛》考了一个单调栈,是个送分题,老师说今年考单调队列的可能性比较高,2010年考了一个普通的队列模拟,2005年考了一个比较难的《等价表达式》现在还没有做出来,所以就复习复习吧!

 

①链表:

这个不准备看了,没什么东西,就个约瑟夫问题,很简单,过了。

 

②栈: 

  这个很有意思,主要在于单调栈可以维护局部最优的性质,OJ1859子序列累加和》。

题目如下:

描述 Description 

     x在学习数列。他想到一个数学问题:

现在有N个数的数列。现在你定义一个子序列是数列的连续一部分,子序列的值是这个子序列中最大值和最小值之差。

给你这N个数,小x想知道所有子序列的值得累加和是多少。

输入格式 Input Format   

     第一行一个整数N (2  N  300 000)

接下来N行,每行一个整数Ai,表示数列的值,保证0<Ai<= 100000 000

输出格式 Output Format  

    一个整数,所求的子序列值得累加和。

样例输入 Sample Input   

4

3

1

7

2

 

样例输出 Sample Output  

31

 

coci2012#3一道题目。有一定难度,做了好几个月才做出来。

本质就是个单调栈,不过求总数的方法比较精妙。

下面附AC代码:

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cstdlib>
using namespace std;
long long n,ans,top=0;
int a[300010],max1[300010],max2[300010],min1[300010],min2[300010],t[300010];
int main()
{
   //freopen("a.txt","r",stdin);
   cin>>n;
   for(int i=1;i<=n;i++)
      scanf("%d",&a[i]);
   t[top=0]=0;//大简化!!!!!!!!!!!!!!!!!!!!
   for(int i=1;i<=n;i++)
   {
      while(a[i]>a[t[top]]&&top>0)
         top--;
      max1[i]=t[top]+1;
      t[++top]=i;
   }
   t[top=0]=n+1;
   for(int i=n;i>=1;i--)
   {
      while(a[i]>=a[t[top]]&&top>0)
         top--;
      max2[i]=t[top]-1; 
      t[++top]=i;
   }
   for(int i=1;i<=n;i++)
      ans+=(long long)(a[i])*(i-max1[i]+1)*(max2[i]-i+1);
   t[top=0]=0;
   for(int i=1;i<=n;i++)
   {
      while(a[i]<a[t[top]]&&top>0)
         top--;
      min1[i]=t[top]+1;
      t[++top]=i;
   }
   t[top=0]=n+1;
   for(int i=n;i>=1;i--)
   {
      while(a[i]<=a[t[top]]&&top>0)
         top--;
      min2[i]=t[top]-1;
      t[++top]=i;
   }
   for(int i=1;i<=n;i++)
      ans-=(long long)(a[i])*(i-min1[i]+1)*(min2[i]-i+1);
   cout<<ans<<endl;
}


去年的积木大赛很简单,简单看看吧!


 

2014.9.19

    接着昨天的来,

③队列:

    2010年考了道简单的队列,今年估计会考单调队列,重点复习一下。

    单调队列中每个元素一般储存两个值:

(1)    原数列中的下标;

(2)    它在动态规划中的状态值。 

单调队列需要保持两个值同时单调。

单调队列和单调栈相似,但和单调栈不同,栈更在乎局部最优解,而队列更在乎乎连续区间的最优解,或者说栈维护局部最优值而队列维护整体最优值,单调队列对动态规划的优化经常考,这次也很可能。

    单调队列有一道OJ1157window》。

题目描述:

     给你一个长度为 N 的数组,一个长为 K 的滑动的窗体从最左移至最右端,

你只能见到窗口的K个数,每次窗体向右移动一位,如下表:

CCF NOIP2014前的复习(9.18~9.19) - 哭,等谁回眸 - 哭,等谁回眸

 

你的任务是找出窗口在各位置时的 max value,min value.

输入格式 Input Format   

      1  n,k,

 2 行为长度为 n 的数组

输出格式 Output Format  

    2 行,

 1 行每个位置的 min value,

 2 行每个位置的 max value

样例输入:

8 3

1 3 -1 -3 5 3 6 7

样例输出:

-1 -3 -3 -3 3 3

3 3 5 5 6 7

 

20% n<=500;  

50%:  n<=100000;

100%:  n<=1000000;

 

不难,现在比较好理解了。

代码如下:

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,k;
int a[1000086],t1[1000086],t2[1000086];
int head1=0,head2=0,tail1=0,tail2=0;
int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>k;
	for(int i=1;i<=n;i++)
		cin>>a[i];//以下解决每k个数中最小值的问题。
	for(int i=2;i<k;i++)//预先处理一下前k-1个数。
	{
		while((a[i]<a[t1[tail1]])&&(tail1>=head1)) tail1--;
		t1[++tail1]=i;
	}
	for(int i=k;i<=n;i++)//继续处理剩下的数。
	{
		if(i-t1[head1]==k) head1++;
		while((a[i]<a[t1[tail1]])&&(tail1>=head1)/*这里还要考虑到队头队尾大小的问题*/) tail1--;
		t1[++tail1]=i;//将下标记录。
		cout<<a[t1[head1]]<<' ';
	}
	cout<<endl;
	//以下解决每k个数中最大值的问题。
	for(int i=2;i<k;i++)//方法同上,只是一个是单调减区间,一个是单调增区间。
	{
		while((a[i]>a[t2[tail2]])&&(tail2>=head2)) tail2--;
		t2[++tail2]=i;
	}
	for(int i=k;i<=n;i++)
	{
		if(i-t2[head2]==k) head2++;
		while((a[i]>a[t2[tail2]])&&(tail2>=head2)) tail2--;
		t2[++tail2]=i;
		cout<<a[t2[head2]]<<' ';
	}
}

队列的作用很大,这里先简单复习,过段时间还要再复习。


  今天时间不多,先写到这儿,明天再说!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值