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) 它在动态规划中的状态值。
单调队列需要保持两个值同时单调。
单调队列和单调栈相似,但和单调栈不同,栈更在乎局部最优解,而队列更在乎乎连续区间的最优解,或者说栈维护局部最优值而队列维护整体最优值,单调队列对动态规划的优化经常考,这次也很可能。
单调队列有一道OJ1157《window》。
题目描述:
给你一个长度为 N 的数组,一个长为 K 的滑动的窗体从最左移至最右端,
你只能见到窗口的K个数,每次窗体向右移动一位,如下表:
你的任务是找出窗口在各位置时的 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]]<<' '; } }
队列的作用很大,这里先简单复习,过段时间还要再复习。
今天时间不多,先写到这儿,明天再说!