单调队列

单调队列
单调队列

单调队列典型程序:

滑动窗口

现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。例如:The array is [1 3 -1 -3 5 3 6 7], and k = 3.

窗口最小值最大值

[1 3 -1] -3 5 3 6 7 -1 3

1 [3 -1 -3]5 3 6 7 -3 3

1 3 [-1 -3 5] 3 6 7 -3 5

1 3 -1 [-3 5 3] 6 7 -3 5

1 3 -1 -3[5 3 6] 7 3 6

1 3 -1 -35 [3 6 7] 3 7

输入格式

输入一共有两行,第一行为n,k。第二行为n个数(

输出格式

输出共两行,第一行为每次窗口滑动的最小值第二行为每次窗口滑动的最大值

样例输入

8 31 3 -1 -3 5 3 6 7

样例输出

-1 -3 -3 -3 3 33 3 5 5 6 7

维护方式(以最大值为例):

队列单调递减,故队首即为所求

每当遇到新元素,当队尾不大于新元素时队尾出队(貌似可用二分搜索优化),新元素进队,当队首过期时队首出队。

AC代码:

var

a,b,c,f1,f2:array[1..1000000] of longint;

n,i,j,k,p1,p2,q1,q2:longint;

begin

readln(n,k);

for i:=1 to n do read(a[i]);

if k=1 then

begin

for i:=1 to n-1 do write(a[i],' ');

writeln(a[n]);

for i:=1 to n-1 do write(a[i],' ');

writeln(a[n]);

halt;

end;

p1:=1;

q1:=1;

p2:=1;

q2:=1;

b[1]:=1;

c[1]:=1;

for i:=2 to n do

begin

while (q1>=p1)and(a[i]<=a[b[q1]]) do dec(q1);

inc(q1);

b[q1]:=i;

while b[p1]

f1[i]:=a[b[p1]];

while (q2>=p2)and(a[i]>=a[c[q2]]) do dec(q2);

inc(q2);

c[q2]:=i;

while c[p2]

f2[i]:=a[c[p2]];

end;

for i:=k to n-1 do write(f1[i],' ');

writeln(f1[n]);

for i:=k to n-1 do write(f2[i],' ');

writeln(f2[n]);

end.

单调队列,顾名思义是指队列内的元素是有序的,队头为当前的最大值(单调递减队列)或最小值(单调递增序列),以单调递减队列为例来看队列的入队和出队操作:

1、入队:

如果当前元素要进队,把当前元素和队尾元素比较,如果当前元素小于队尾元素,那么当前元素直接进队,如果当前元素大于队尾元素,那么队尾出队,将当前元素和新的队尾再做比较,直到当前元素大于队尾元素或者队列为空。单调队列只能在队尾插入元素,队尾和队头都可以删除元素。

2、出队:

出队直接取队头即可,因为用单调队列就是为了取最值,而队头就是最值。

例子:将数组a[] = {3, 5, 2, 8, 1, 4, 7}依次入队,并保证队列为一个单调递减队列。

?3入队,队列为空直接入队,队列元素为:3;

?5入队,5和队尾比较,5大于3,3出队,队为空,5入队,队列元素为:5;

?2入队,2和队尾比较,2小于5,直接入队,队列元素为:5,2;

?8入队,8和队尾比较,2出队,8再和队尾比较,5出队,队为空,8入队,队列元素为:8;

?1入队,...,队列元素为:8,1;

?4入队,...,队列元素为:8,4;

?7入队,...,队列元素为:8,7。

实例应用:

1、给定一个数组a[]和一个长度为k的滑动窗口,该窗口从最左端移到最右端,找出窗口在每个位置是的最大值。

例如:a[] = {7, 3, 2, 5, 6},k = 3。我们可以维护一个单调递减队列,这样对于每个位置,当前队列的队头就是最大值,只不过在入队的时候要检查一下队头的下标是否已经超出窗口的范围,如果超出就删除队头元素即可。为了方便写代码,单调队列很多时候保存的是下标,而不是数值本身。

?i = 0,初始队列为空,7入队,队列为:{ 0(7) },(0为下标,括号为下标对应的值),窗口区间为[0],最大值为队头a[0] = 7;

?i = 1,队头下标没有超出k,3入队,队列为:{ 0(7), 1(3) },窗口区间为[0,1],最大值为队头a[0] = 7;

?i = 2,队头下标没有超出k,2入队,队列为:{ 0{7), 1(3), 2(2) },窗口区间为[0,1,2],最大值为队头a[0] = 7;

?i = 3,队头下标为0超出k,删除队头,5入队,队列为:{ 3(5) },窗口区间为[1,2,3],最大值为队头a[3] = 5;

?i = 4,队头下标没有超出k,6入队,队列为:{ 4(6) },窗口区间为[2, 3, 4],最大值为队头a[4] = 6;

窗口继续向右滑动,如果当前队头下标超出范围就删除队头,然后去队头,没有超出范围就直接取队头。

这样整个算法就是O(n)的。

procedure work; {设原始数据存入q[i]中}

V ar i,top,tail:longint;

begin

top:=1; tail:=1;

queue[top]:=1; //队头指向q[i]中第一个数

for i:=2 to k do //前k个数的初始队列

begin

while (top<=tail)and(q[queue[tail]]>=q[i]) do

dec(tail);//队尾元素大于当前元素出队

inc(tail); //当前元素入队

queue[tail]:=i;

end;

min[1]:=q[queue[top]]; //第1窗口最小值

for i:=2 to n-k+1 do //窗口左端位置

begin

if queue[top]

while (top<=tail)and(q[queue[tail]]>=q[i+k-1]) do

dec(tail); //队尾元素大于当前窗口右端元素,出队

inc(tail);

queue[tail]:=i+k-1;//当前窗口右端元素入队

min[i]:=q[queue[top]];//取当前窗口最小值

end;

end;

const

maxn=1000000;

var

q1,q2:array[0..maxn shl 1] of longint;

a,b,p:array[1..maxn]of longint;

n,m:longint;

f1,f2:text;

procedure init;

var

i,j:longint;

begin

readln(n,m);

for i:=1 to n do read(f1,p[i]);

end;

procedure main;

var

i,j,h1,t1,h2,t2:longint;

begin

q1[1]:=1;q2[1]:=1;

h1:=1;t1:=1;h2:=1;t2:=1;

for i:=2 to m do

begin

//while (q1[h1]<=i-m)and(h1<=t1) do inc(h1);

//while (q2[h2]<=i-m)and(h2<=t2) do inc(h2);

while (p[q1[t1]]>=p[i])and(t1>=h1) do dec(t1);

inc(t1);q1[t1]:=i;

while (p[q2[t2]]<=p[i])and(t2>=h2) do dec(t2);

inc(t2);q2[t2]:=i;

end;

a[1]:=p[q1[h1]];b[1]:=p[q2[h2]];

for i:=m+1 to n do

begin

while (q1[h1]<=i-m)and(h1<=t1) do inc(h1); //if (q1[h1]=i-m) and (h1<=t1) then inc(h1) while (q2[h2]<=i-m)and(h2<=t2) do inc(h2);

while (p[q1[t1]]>=p[i])and(t1>=h1) do dec(t1);

inc(t1);q1[t1]:=i;

while (p[q2[t2]]<=p[i])and(t2>=h2) do dec(t2);

inc(t2);q2[t2]:=i;

a[i-m+1]:=p[q1[h1]];b[i-m+1]:=p[q2[h2]];

end;

end;

procedure print;

var

i,j:longint;

begin

for i:=1 to n-m do write(a[i],' ');

writeln(a[n-m+1]);

for i:=1 to n-m do write(b[i],' ');

writeln(b[n-m+1]);

end;

begin

assign(f1,wind.in);reset(f1); assign(f2,win.out);rewrite(f2); init;

main;

print;

close(f1);close(f2);

end.

单调队列及其应用

单调队列,望文生义,就是指队列中的元素是单调的。如:{a1,a2,a3,a4……an}满足

a1<=a2<=a3……<=an,a序列便是单调递增序列。同理递减队列也是存在的。

单调队列的出现可以简化问题,队首元素便是最大(小)值,这样,选取最大(小)值的复杂度便为o(1),由于队列的性质,每个元素入队一次,出队一次,维护队列的复杂度均摊下来便是o(1)。

如何维护单调队列呢,以单调递增序列为例:

1、如果队列的长度一定,先判断队首元素是否在规定范围内,如果超范围则增长队首。

2、每次加入元素时和队尾比较,如果当前元素小于队尾且队列非空,则减小尾指针,队尾元素依次出队,直到满足队列的调性为止

要特别注意头指针和尾指针的应用。

下面介绍单调队列的具体应用:

一、单调队列的直接应用

1.合并果子

【问题描述】

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。

【输入文件】

输入文件fruit.in包括两行,第一行是一个整数n(1 <= n <= 10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1 <= ai <= 20000)是第i种果子的数目。【输出文件】

输出文件fruit.out包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于231。

【样例输入】

3

1 2 9

【样例输出】

15

【数据规模】

对于30%的数据,保证有n <= 1000;

对于50%的数据,保证有n <= 5000;

对于全部的数据,保证有n <= 10000。

分析:

这个题目非常的经典,发放也很多,可以采用快排或者堆,其思想都是选取当前最小的两个堆进行合并。复杂度均为O(nlogn),如果用有序队列维护,时间复杂度为O(n)。

每次选取进行合并的两堆,不是最先给定的堆,就是合并最初堆若干次后得到的新堆,所以需要维护两个单调递增队列,一个队列存最初给定的堆的值(1),一个存合并后得到的新值(2)。每次选择时有三种状态:

1.选取队一的队首两个

2.选取队2的的队首两个

3.选取二者队首各一个

只需对每个队列的指针做相应的更改。

特别注意初始化。

这道题很好的运用了题目中决策的单调性,对初始对经行排序,保证了其单调性。而对于新产生的堆来说,一旦有新元素加入其中,则新元素一定大于原有元素。(很显然,由于队列1的单调性)。

也就是说,队列的单调性是自然而然的。是不需要维护的。要善于观察分析,才能发现。

队列的基本操作代码

队列的基本操作代码: #include #include #define MAXQSIZE 100 #define OVERFLOW 0 #define ERROR 0 #define OK 1 typedef int QElemType; typedef int Status; typedef struct { QElemType *base; int front; int rear; int tag; }SqQueue; Status InitQueue(SqQueue &Q) { Q.base=(QElemType*)malloc(MAXQSIZE*sizeof(QElemType)); if(!Q.base) exit(OVERFLOW);//存储分配失败 Q.front=Q.rear=0; tag=0; return OK; } int QueueLength(SqQueue Q) { return (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;//返回Q的元素个数,即队列的长度} Status EnQueue(SqQueue &Q,QElemType e) { if((Q.rear+1)%MAXQSIZE==Q.front) return ERROR;//队列满 Q.base[Q.rear]=e; Q.rear=(Q.rear+1)%MAXQSIZE; return OK; } Status DeQueue(SqQueue &Q,QElemType &e) { if(Q.front==Q.rear) return ERROR; e=Q.base[Q.front];

单调队列的应用

(烽火传递)烽火台又称烽燧 单调队列及其应用 单调队列,望文生义,就是指队列中的元素是单调的。如:{a1,a2,a3,a4……an}满足a1<=a2<=a3……<=an,a序列便是单调递增序列。同理递减队列也是存在的。 单调队列的出现可以简化问题,队首元素便是最大(小)值,这样,选取最大(小)值的复杂度便为O(1),由于队列的性质,每个元素入队一次,出队一次,维护队列的复杂度均摊下来便是O(1)。 如何维护单调队列呢,以单调递增序列为例: 1、如果队列的长度一定,先判断队首元素是否在规定范围内,如果超范围则增长队首。 2、每次加入元素时和队尾比较,如果当前元素小于队尾且队列非空,则减小尾指针,队尾元素依次出队,直到满足队列的调性为止 要特别注意头指针和尾指针的应用。 下面介绍单调队列的具体应用: 一、单调队列的直接应用 1.合并果子 【问题描述】 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。 因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。 例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以多多总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。 【输入文件】 输入文件fruit.in包括两行,第一行是一个整数n(1 <= n <= 10000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1 <= ai <= 20000)是第i种果子的数目。 【输出文件】 输出文件fruit.Out包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于231。 【样例输入】 3 1 2 9 【样例输出】 15 【数据规模】 对于30%的数据,保证有n <= 1000; 对于50%的数据,保证有n <= 5000; 对于全部的数据,保证有n <= 10000。 分析: 这个题目非常的经典,发放也很多,可以采用快排或者堆,其思想都是选取当前最小的两个堆进行合并。复杂度均为O(nlOgn),如果用有序队列维护,时间复杂度为O(n)。 每次选取进行合并的两堆,不是最先给定的堆,就是合并最初堆若干次后得到的新堆,所以需要维护两个单调递增队列,一个队列存最初给定的堆的值(1),一个存合并后得到的新值(2)。 每次选择时有三种状态:

队列的基本操作及其应用

广西工学院计算机学院 《数据结构》课程实验报告书实验四队列的基本操作及其应用 学生姓名:李四 学号:2012 班级:计Y124 指导老师:王日凤 专业:计算机学院软件学院 提交日期:2013年6月20日

1.实验目的 1)通过对队列特点的分析,掌握队列的存储结构及其基本操作,学会定义队列的顺序存储结构和链式存储结构,在实际问题中灵活运用。 2)掌握队列先进先出的特点,掌握队列的基本操作,如出队列、入队列、判队列空、判队列满等,熟悉各种操作的实现方法。 3)通过具体的应用实例,进一步熟悉和掌握队列的实际应用。 2.实验内容 (1)建立一个含n个数据的队列,实现队列的基本操作。包括: ?//1. 初始化,构造一个空队列 void initQueue(Queue &Q) ?//2. 判断队列空, 空则返回true bool QueueEmpty(seqQueue &Q) ?//3. 判断队列满, 满则返回true bool QueueFull(seqQueue &Q) ?//4. 取队头元素, 用x返回队头元素,返回true;空队列则返回false Bool QueueHead(seqQueue &Q, elementType &x) ?//5. 入队列,在队尾插入新元素x (流程图) bool pushQueue (seqQueue &Q, elementType x) ?//6. 出队列,用x带回队头元素,并在队头删除,返回true,队列空则返回false(流程图)bool popQueue (seqQueue &Q, elementType &x) ?//7. 输出队列,从队头到队尾依次输出 void printQueue(seqQueue Q) (2)队列应用:利用队列操作打印杨辉三角形的前n行(如n=7)。 3.实验要求 (1)上机前交实验源程序(纸质版),由学习委员统一收好交老师(附上不交同学名单)。 (2)用一切你能想到的办法解决遇到的问题,培养解决问题的能力。 (3)实验课上进行答辩。 (4)实验报告当场交。报告内容包括:实验目的、实验内容、实验代码、实验输入输出结果以及实验体会供五部分。

ACM经典算法及配套练习题

POJ上的一些水题(可用来练手和增加自信) (poj3299,poj2159,poj2739,poj1083,poj2262,poj1503,poj3006,p oj2255,poj3094) 初期: 一.基本算法: (1)枚举. (poj1753,poj2965) (2)贪心(poj1328,poj2109,poj2586) (3)递归和分治法. (4)递推. (5)构造法.(poj3295) (6)模拟法.(poj1068,poj2632,poj1573,poj2993,poj2996) 二.图算法: (1)图的深度优先遍历和广度优先遍历. (2)最短路径算法(dijkstra,bellman-ford,floyd,heap+dijkstra) (poj1860,poj3259,poj1062,poj2253,poj1125,poj2240) (3)最小生成树算法(prim,kruskal) (poj1789,poj2485,poj1258,poj3026) (4)拓扑排序(poj1094) (5)二分图的最大匹配(匈牙利算法) (poj3041,poj3020) (6)最大流的增广路算法(KM算法). (poj1459,poj3436) 三.数据结构. (1)串(poj1035,poj3080,poj1936) (2)排序(快排、归并排(与逆序数有关)、堆排) (poj2388,poj2299) (3)简单并查集的应用. (4)哈希表和二分查找等高效查找法(数的Hash,串的Hash) (poj3349,poj3274,POJ2151,poj1840,poj2002,poj2503) (5)哈夫曼树(poj3253) (6)堆 (7)trie树(静态建树、动态建树) (poj2513) 四.简单搜索 (1)深度优先搜索(poj2488,poj3083,poj3009,poj1321,poj2251) (2)广度优先搜索(poj3278,poj1426,poj3126,poj3087.poj3414) (3)简单搜索技巧和剪枝(poj2531,poj1416,poj2676,1129) 五.动态规划 (1)背包问题. (poj1837,poj1276) (2)型如下表的简单DP(可参考lrj的书page149): 1.E[j]=opt{D+w(i,j)} (poj3267,poj1836,poj1260,poj2533) 2.E[i,j]=opt{D[i-1,j]+xi,D[i,j-1]+yj,D[i-1][j-1]+zij} (最长公共子序列) (poj3176,poj1080,poj1159) 3.C[i,j]=w[i,j]+opt{C[i,k-1]+C[k,j]}.(最优二分检索树问题) 六.数学 (1)组合数学:

试验 --循环队列的基本操作及应用

数据结构实验报告 ----试验三循环队列的基本操作及应用 一、问题描述: 熟悉并掌握循环队列的相关操作,自己设计程序,实现循环队列的构造、清空、销毁及队列元素的插入和删除等相关操作。 二、数据结构设计: #define MAXQSIZE 10 //最大队列长度 struct SqQueue { QElemType *base; //初始化动态分配存储空间 Int front; // 头指针,若队列不空,只想对列头元素 int rear; //尾指针,若队列不空,指向队列尾元素的 //下一个位置 }; 三、功能设计: 程序中所涉及到的函数如下: Status InitQueue(SqQueue &Q) //构造一个空队列Q Status DestroyQueue(SqQueue &Q) //销毁队列Q,Q不再存在 Status ClearQueue(SqQueue &Q) //将Q清为空队列 Status QueueEmpty(SqQueue Q) //若队列Q为空队列,则 //返回TRUE,否则返回FALSE int QueueLength(SqQueue Q) //返回Q的元素个数,即队列长度Status GetHead(SqQueue Q,QElemType &e)//若队列不空,则用e返回Q的对 //头元素,并返回OK,否则返回ERROR Status EnQueue(SqQueue &Q,QElemType e)//插入元素e为Q的新的队尾元素Status DeQueue(SqQueue &Q,QElemType &e)//若队列不空,则删除Q的队头 //元素,用e返回其值,并返回 //OK,否则返回ERROR Status QueueTraverse(SqQueue Q,void(*vi)(QElemType))//从队头到队尾依次 //对队列Q中每个元素调用函数 //vi()。一旦vi失败,则操作失败四、源程序: // c1.h (程序名) #include #include #include // malloc()等 #include // INT_MAX等 #include // EOF(=^Z或F6),NULL

栈和队列的基本操作实现及其应用

实验二栈和队列的基本操作实现及其应用 一_一、实验目的 1、熟练掌握栈和队列的基本操作在两种存储结构上的实现。 一_二、实验内容 题目一、试写一个算法,判断依次读入的一个以@为结束符的字符序列,是否为回文。所谓“回文“是指正向读和反向读都一样的一字符串,如“321123”或“ableelba”。 相关常量及结构定义: #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10 typedef int SElemType; typedef struct SqStack { SElemType *base; SElemType *top; int stacksize; }SqStack; 设计相关函数声明: 判断函数:int IsReverse() 栈:int InitStack(SqStack &S )

int Push(SqStack &S, SElemType e ) int Pop(SqStack &S,SElemType &e) int StackEmpty(s) 一_三、数据结构与核心算法的设计描述 1、初始化栈 /* 函数功能:对栈进行初始化。参数:栈(SqStack S)。 成功初始化返回0,否则返回-1 */ int InitStack(SqStack &S) { S.base=(SElemType *)malloc(10*sizeof(SElemType)); if(!S.base) //判断有无申请到空间 return -1; //没有申请到内存,参数失败返回-1 S.top=S.base; S.stacksize=STACK_INIT_SIZE; S.base=new SElemType; return 0; } 2、判断栈是否是空 /*函数功能:判断栈是否为空。参数; 栈(SqStack S)。栈为空时返回-1,不为空返回0*/ int StackEmpty(SqStack S) { if(S.top==S.base) return -1; else return 0; } 3、入栈 /*函数功能:向栈中插入元素。参数; 栈(SqStack S),元素(SElemtype e)。成功插入返回0,否则返回-1 */ int Push(SqStack &S,SElemType e) { if(S.top-S.base>=S.stacksize) { S.base=(SElemType *)realloc(S.base,(S.stacksize+1) * sizeof(SElemType));

NOI国家集训队论文分类(至2008)(摘抄自C博客)

摘抄自C博客 组合数学 计数与统计 2001 - 符文杰:《Pólya原理及其应用》 2003 - 许智磊:《浅谈补集转化思想在统计问题中的应用》 2007 - 周冬:《生成树的计数及其应用》 2008 - 陈瑜希《Pólya计数法的应用》 数位问题 2009 - 高逸涵《数位计数问题解法研究》 2009 - 刘聪《浅谈数位类统计问题》 动态统计 2004 - 薛矛:《解决动态统计问题的两把利刃》 2007 - 余江伟:《如何解决动态统计问题》 博弈 2002 - 张一飞:《由感性认识到理性认识——透析一类搏弈游戏的解答过程》2007 - 王晓珂:《解析一类组合游戏》 2009 - 曹钦翔《从“k倍动态减法游戏”出发探究一类组合游戏问题》 2009 - 方展鹏《浅谈如何解决不平等博弈问题》 2009 - 贾志豪《组合游戏略述——浅谈SG游戏的若干拓展及变形》 母函数 2009 - 毛杰明《母函数的性质及应用》 拟阵 2007 - 刘雨辰:《对拟阵的初步研究》 线性规划 2007 - 李宇骞:《浅谈信息学竞赛中的线性规划——简洁高效的单纯形法实现与应用》 置换群 2005 - 潘震皓:《置换群快速幂运算研究与探讨》 问答交互 2003 - 高正宇:《答案只有一个——浅谈问答式交互问题》 猜数问题 2003 - 张宁:《猜数问题的研究:<聪明的学生>一题的推广》

2006 - 龙凡:《一类猜数问题的研究》 数据结构 数据结构 2005 - 何林:《数据关系的简化》 2006 - 朱晨光:《基本数据结构在信息学竞赛中的应用》 2007 - 何森:《浅谈数据的合理组织》 2008 - 曹钦翔《数据结构的提炼与压缩》 结构联合 2001 - 高寒蕊:《从圆桌问题谈数据结构的综合运用》 2005 - 黄刚:《数据结构的联合》 块状链表 2005 - 蒋炎岩:《数据结构的联合——块状链表》 2008 - 苏煜《对块状链表的一点研究》 动态树 2006 - 陈首元:《维护森林连通性——动态树》 2007 - 袁昕颢:《动态树及其应用》 左偏树 2005 - 黄源河:《左偏树的特点及其应用》 跳表 2005 - 魏冉:《让算法的效率“跳起来”!——浅谈“跳跃表”的相关操作及其应用》 2009 - 李骥扬《线段跳表——跳表的一个拓展》 SBT 2007 - 陈启峰:《Size Balance Tree》 线段树 2004 - 林涛:《线段树的应用》 单调队列 2006 - 汤泽:《浅析队列在一类单调性问题中的应用》 哈希表 2005 - 李羽修:《Hash函数的设计优化》 2007 - 杨弋:《Hash在信息学竞赛中的一类应用》 Splay 2004 - 杨思雨:《伸展树的基本操作与应用》

数据结构 栈和队列的基本操作实现及其应用

实验二栈和队列的基本操作实现及其应用 一、实验目的 1、熟练掌握栈和队列的基本操作在两种存储结构上的实现。 2、会用栈和队列解决简单的实际问题。 二、实验内容(可任选或全做) 题目一、试写一个算法,判断依次读入的一个以@为结束符的字符序列, 是否为回文。所谓“回文“是指正向读和反向读都一样的一字符串,如“321123”或“ableelba”。 相关常量及结构定义: # define STACK_INIT_SIZE 100 # define STACKINCREMENT 10 # define OK 1 # define ERROR 0 typedef int SElemType; //栈类型定义 typedef struct SqStack { SElemType *base; SElemType *top; int stacksize; }SqStack; 设计相关函数声明: 判断函数:int IsReverse() 栈:int InitStack(SqStack &S ) int Push(SqStack &S, SElemType e ) int Pop(SqStack &S,SElemType &e) int StackEmpty(s) 题目二、编程模拟队列的管理,主要包括: 出队列、 入队、 统计队列的长度、 查找队列某个元素e、 及输出队列中元素。 [实现提示]:参考教材循环队列的有关算法,其中后两个算法参考顺序表的实现。 题目三、Rails

Description There is a famous railway station in PopPush City. Country there is incredibly hilly. The station was built in last century. Unfortunately, funds were extremely limited that time. It was possible to establish only a surface track. Moreover, it turned out that the station could be only a dead-end one (see picture) and due to lack of available space it could have only one track. The local tradition is that every train arriving from the direction A continues in the direction B with coaches reorganized in some way. Assume that the train arriving from the direction A has N <= 1000 coaches numbered in increasing order 1, 2, ..., N. The chief for train reorganizations must know whether it is possible to marshal coaches continuing in the direction B so that their order will be a1, a2, ..., aN. Help him and write a program that decides whether it is possible to get the required order of coaches. You can assume that single coaches can be disconnected from the train before they enter the station and that they can move themselves until they are on the track in the direction B. You can also suppose that at any time there can be located as many coaches as necessary in the station. But once a coach has entered the station it cannot return to the track in the direction A and also once it has left the station in the direction B it cannot return back to the station. Input The input consists of blocks of lines. Each block except the last describes one train and possibly more requirements for its reorganization. In the first line of the block there is the integer N described above. In each of the next lines of the block there is a permutation of 1, 2, ..., N. The last line of the block contains just 0. The last block consists of just one line containing 0. Output

顺序队列的基本操作

#include #include #include #include #define QueueSize 50 typedef char QueueData; typedef struct queue { QueueData data[QueueSize]; int rear,front; }SeqQueue; void Menu() { printf("\n"); printf("|…………………………………………|\n"); printf("| 1、建立|\n"); printf("| |\n"); printf("| 2、显示|\n"); printf("| |\n"); printf("| 3、入队|\n"); printf("| |\n"); printf("| 4、出队|\n"); printf("| |\n"); printf("| 5、取队头元素|\n"); printf("| |\n"); printf("| 6、退出|\n"); printf("|…………………………………………|\n"); printf("\n"); printf("请选择菜单项,按回车键完成选择:"); } //模块1 建立 void Set(SeqQueue *&Q) { Q=(SeqQueue*)malloc(sizeof(SeqQueue)); if(Q==NULL) { printf("存储空间分配失败!\n"); exit(1); } else { printf("存储空间分配成功!\n"); } Q->front=Q->rear=-1; //置空队列

数据结构总结

转载自South_wind的专栏 常见的数据结构运用总结 考虑到Obsidian三个成员的擅长领域,这段时间都在做杂题,算是学习各种算法吧,趁现在休息的时间,而且大家马上要备战今年的比赛了,写写自己专攻方面的一些心得吧 扯开线段树、平衡树这些中高级的东西,先说说基础的数据结构 栈 算是代码量最小的数据结构?出栈进栈都只有一句话而已 常见用途: 消去一个序列中的相同元素(做法大家应该都知道了吧,见过很多次了) 维护一个单调的序列(所谓的单调栈,dp的决策单调?) 表达式求值(经典的栈运用,如果使用的很熟悉的话,可以处理一元、二元运算,不过最近没见过类似的题目了) 用于辅助其他算法(计算几何中的求凸包) 队列 队列应该还是很常见的数据结构了,如果专攻图论的话,spfa应该是写烂了的 这里说到的队列,是狭义的普通的队列和循环队列,不包括后面讲的一些变形 注意循环队列的写法,尽量不要使用取模运算,不然的话,遇到不厚道的出题者,可以把取模的循环队列卡到死 常见用途: 主要用于辅助其他算法,比如说spfa,bfs等(建议习惯用stl的孩子手写queue,毕竟就几行代码而已,偷懒会付出代价的。。。) 双端队列 如果写dp写的多的话,这个东西应该还是算是比较基础的东西了,双端队列多用于维护一个满足单调性的队列 还是建议手写,stl的deque使用块状链表写的,那东西的复杂度是O(Nsqrt(N))的,不要被迷惑了。 常见用途: dp的单调性优化,包括单调队列优化和斜率优化,都要用到这个结构 计算几何中的算法优化,比如半平面交 树的分治问题中利用单调队列减少转移复杂度 链表Dancing Links 写图论的不要告诉我不会写这货,链表可以写单双向,循环非循环的,高级点儿的可以考虑十字链表,麻花链表 不过链表可以说是树形结构的基础,如果这个掌握的不好,那么树形结构写起来就会很纠结 链表的优势在于可以O(1)的插入删除,如果要求插入的位置只是在序列的两端的话,这个数据结构是最方便的了(无视双端队列) hash表就是用链表实现的,熟悉hash的同学可以试试看怎么使你的hash效率提高

单调队列在信息学竞赛中的应用

单调队列在信息学竞赛中的应用 一、概念介绍 1、双端队列 双端队列是一种线性表,是一种特殊的队列,遵守先进先出的原则。双端队列支持一下4种操作: (1)从队首删除 (2)从队尾删除 (3)从队尾插入 (4)查询线性表中任意一元素的值 2、单调队列 单调队列是一种特殊的双端队列,其内部元素具有单调性。最大队列与最小队列是两种比较常用的单调队列,其内部元素分别是单调递减和单调递增的。 单调队列的常用操作如下: (1)插入:若新元素从队尾插入后会破坏单调性,则删除队尾元素,直到插入后不再 破坏单调性为止,再将其插入单调队列。 (2)获取最优(最大、最小)值:访问队首元素 二、单调队列的应用 例题1:Sliding window1 给你一个长度为N的数组,一个长为K的滑动的窗体从最左移至最右端, 你的任务是找出窗口在各位置时的max value,min value. 输入格式: 第1行n,k,第2行为长度为n的数组 输出格式: 2行,第1行每个位置的min value,第2行每个位置的max value 样例: window.in 8 3 1 3 -1 -3 5 3 6 7 window.out -1 -3 -3 -3 3 3 3 3 5 5 6 7 数据范围: 20%:n<=500; 50%: n<=100000; 100%: n<=1000000;

[题目大意] 给定一个长度为n的数列,求长度为k的定长连续子区间{a1,a2,a3,a4,…,ak-1,ak}{a2,a3,…,ak,ak+1}……中每个区间的最大值和最小值。 【分析】 这道题的模型相当于一个序列上的问题。看到题目,第一想法应该是:先枚举起始元素ax,然后求ax到ax+k-1的最大(小)值。朴素方法为直接扫描,这样,我们就得到了一个复杂度为O(nk)的算法。显然,根据题目的数据规模,这样的算法会超时。 思考一下有没有更优秀的算法? 这道题目有个非常重要的信息,即所有的区间都是等长且连续的,那么对于“相邻”两个区间(l,r)与(l+1,r+1)有些极优美的性质: al,al+1,al+2,…,ar-1,ar,ar+1 以最大值为例: 我们注意到,在区间(l,r)中 Max(al,al+1,al+2,…,ar-1,ar) =max(al,max(al+1,al+2,…,ar-1,ar)) 在区间(l+1,r+1)中 Max(al+1,al+2,…,ar-1,ar,ar+1) =max(ar+1,max(al+1,al+2,…,ar-1,ar)) 两个式子中有相同的部分max(al+1,al+2,…,ar-1,ar),经验告诉我们,区间(l,r)中最大值落在(l+1,r)区间的概念很大。那么,在求(l+1,r+1)的最值时,我们完全没有必要再扫描一次。只有当上一次的最大值落在了al上时才需要重新扫描,这样,算法得到了极大的优化。 继续考虑这样一个问题,以最大值为例,对于任意l<=i<=j<=r,如果ai

栈和队列的基本操作讲解

《数据结构与算法》实验报告 专业班级姓名学号 实验项目 实验二栈和队列的基本操作。 实验目的 1、掌握栈的基本操作:初始化栈、判栈为空、出栈、入栈等运算。 2、掌握队列的基本操作:初始化队列、判队列为空、出队列、入队列等运算。 实验内容 题目1: 进制转换。利用栈的基本操作实现将任意一个十进制整数转化为R进制整数 算法提示: 1、定义栈的顺序存取结构 2、分别定义栈的基本操作(初始化栈、判栈为空、出栈、入栈等) 3、定义一个函数用来实现上面问题: 十进制整数X和R作为形参 初始化栈 只要X不为0重复做下列动作 将X%R入栈X=X/R 只要栈不为空重复做下列动作 栈顶出栈输出栈顶元素 题目2: 利用队列的方式实现杨辉三角的输出。 算法设计分析 (一)数据结构的定义 1、栈的应用 实现十进制到其他进制的转换,该计算过程是从低位到高位顺序产生R进制数的各个位数,而打印输出一般从高位到低位进行,恰好与计算过程相反。因此,运用栈先进后出的性质,即可完成进制转换。 栈抽象数据结构描述 typedef struct SqStack /*定义顺序栈*/ { int *base; /*栈底指针*/ int *top; /*栈顶指针*/ int stacksize; /*当前已分配存储空间*/ } SqStack; 2、队列的应用 由于是要打印一个数列,并且由于队列先进先出的性质,肯定要利用已经进队的元素在其出队之前完成杨辉三角的递归性。即,利用要出队的元素来不断地构造新的进队的元素,即在第N行出队的同时,来构造杨辉三角的第N+1行,从而实现打印杨辉三角的目的。

队列抽象数据结构描述 typedef struct SeqQueue { int data[MAXSIZE]; int front; /*队头指针*/ int rear; /*队尾指针*/ }SeqQueue; (二)总体设计 1、栈 (1)主函数:统筹调用各个函数以实现相应功能 int main() (2)空栈建立函数:对栈进行初始化。 int StackInit(SqStack *s) (3)判断栈空函数:对栈进行判断,若栈中有元素则返回1,若栈为空,则返回0。 int stackempty(SqStack *s) (4)入栈函数:将元素逐个输入栈中。 int Push(SqStack *s,int x) (5)出栈函数:若栈不空,则删除栈顶元素,并用x返回其值。 int Pop(SqStack *s,int x) (6)进制转换函数:将十进制数转换为R进制数 int conversion(SqStack *s) 2、队列 (1)主函数:统筹调用各个函数以实现相应功能 void main() (2)空队列建立函数:对队列进行初始化。 SeqQueue *InitQueue() (3)返回队头函数:判断队是否为空,若不为空则返回队头元素。 int QueueEmpty(SeqQueue *q) (4)入队函数:将元素逐个输入队列中。 void EnQueue(SeqQueue *q,int x) (5)出队函数:若队列不空,则删除队列元素,并用x返回其值。 int DeQueue(SeqQueue *q) (6)计算队长函数:计算队列的长度。 int QueueEmpty(SeqQueue *q) (7)输出杨辉三角函数:按一定格式输出杨辉三角。 void YangHui(int n) (三)各函数的详细设计: 1、栈 (1)int main()主函数 定义s为栈类型 调用函数建立空栈

1D1D动态规划优化初步

1D/1D 动态规划优化初步 所谓1D/1D 动态规划,指的是状态数为O(n),每一个状态决策量为O(n)的动态规划方程。直接求解的时间复杂度为O(n 2),但是,绝大多数这样的方程通过合理的组织与优化都是可以优化到O(nlogn)乃至O(n)的时间复杂度的。这里就想讲一讲我对一些比较初步的经典的优化方法的认识。 本文中使用两种方式表示一个函数:f(x)与f[x],用方括号表示的函数值可以在规划之前全部算出(常量),而用圆括号表示的函数值必须在规划过程中计算得到(变量)。无论是什么函数值一经确定,在以后的计算中就不会更改。 经典模型一:1 1 ()min{()[,]}x i f x f i w i ?==+x ] 相信这个方程大家一定是不陌生的。另外,肯定也知道一个关于决策单调性的性质: 假如用k(x)表示状态x 取到最优值时的决策,则决策单调性表述为: ,当且仅当: ,()()i j k i k j ?≤≤ ,对于这个性质的证明读者可以在任意一篇讲述四边形不等式的文章中找到,所以这里不再重复。而且,从实战的角度来看,我们甚至都不需要验证w 函数的这个性质,最经济也是最可靠的方法是写一个朴素算法打出决策表来观察(反正你总还是要对拍)。当然,有的时候题目要求你做一点准备工作,去掉一些明显不可能的决策,然后在应用决策单调性。这是上述性质也许会有点用处。 ,[,][1,1][1,][,1i j w i j w i j w i j w i j ?≤+++≤+++ 正如前文中所述,我们关注的重点是怎样实现决策单调性。有了决策单调性,怎样高效地实现它呢?很容易想到在枚举决策的时候,不需要从1开始,只要从k(x-1)开始就可以了,但这只能降低常数,不可能起到实质性的优化。 另一种想法是从k(x-1)开始枚举决策更新f(x),一旦发现决策u 不如决策u+1来得好,就停止决策过程,选取决策u 作为f(x)的最终决策。这样时间是很大提高了,但可惜是不正确的。决策单调性并没有保证f(j)+w[j,x]有什么好的性质,所以这样做肯定是不对的。 刚才我们总是沿着“f(x)的最优决策是什么”这个思路进行思考,下面我们换一个角度,思考对于一个已经计算出来的状态f(j),“f(j)能够更新的状态有哪些”。这样,每一步过程中某些状态的决策可能不是最优的,但是当算法结束的时候所有状态对应的决策一定是最优的。 一开始,只有f(1)的函数值被计算出来,于是所有状态的当前最优决策都是1。 111111111111111111111111111111111111111111111111111111111111111 现在,显然f(2)的值已经确定了:它的最有决策只能是1。我们用决策2来更新这个决策表。由于决策单调性,我们知道新的决策表只能有这样的形式: 111111111111111111111111111111222222222222222222222222222222 这意味着我们可以使用二分法来查找“转折点”,因为如果在一个点x 上,如果决策2更好,则所有比x 大的状态都是决策2更好;如果x 上决策1更好,则所有比x 小的状态都是

栈和队列基本操作实验报告

栈和队列基本操作实验报告 实验二堆栈和队列基本操作的编程实现【实验目的】 堆栈和队列基本操作的编程实现 要求: 堆栈和队列基本操作的编程实现(2学时,验证型),掌握堆栈和队列的建立、进栈、出栈、进队、出队等基本操作的编程实现,存储结构可以在顺序结构或链接结构中任选,也可以全部实现。也鼓励学生利用基本操作进行一些应用的程序设计。 【实验性质】 验证性实验(学时数:2H) 【实验内容】 内容: 把堆栈和队列的顺序存储(环队)和链表存储的数据进队、出队等运算其中一部分进行程序实现。可以实验一的结果自己实现数据输入、数据显示的函数。 利用基本功能实现各类应用,如括号匹配、回文判断、事物排队模拟、数据逆序生成、多进制转换等。【实验分析、说明过程】 分析: 进栈操作 先创建一个以x为值的新结点p,其data域值为x则进栈操作步骤如下: 将新结点p的指针域指向原栈顶S(执行语句p->next=S)。 将栈顶S指向新结点p(执行语句S=p)。 注:进栈操作的?与?语句执行顺序不能颠倒,否则原S指针其后的链表将丢失。

出栈操作 先将结点栈顶S数据域中的值赋给指针变量*x,则删除操作步骤如下: 结点p 指针域指向原栈顶S(执行语句p=S)。 栈顶S指向其的下一个结点(执行语句S=S->next) 释放p结点空间(执行语句free(p))。 队列分析:用链式存储结构实现的队列称为链队列,一个链队列需要一个队头指针和一个队尾指针才能唯一确定。队列中元素的结构和前面单链表中的结点的结构一样。为了操作方便,在队头元素前附加一个头结点,队头指针就指向头结点。 【思考问题】 1. 栈的顺序存储和链表存储的差异, 答:栈的顺序存储有‘后进先出’的特点,最后进栈的元素必须最先出来,进出栈是有序的,在对编某些需要按顺序操作的程序有很大的作用。

多重背包O(NV)算法详解(使用单调队列)

多重背包O(N*V)算法详解(使用单调队列) 多重背包问题: 有N种物品和容量为V的背包,若第i种物品,容量为v[i],价值为w[i],共有n[i]件。怎样装才能使背包内的物品总价值最大? 网上关于“多重背包”的资料倒是不少,但是关于怎么实现O(N*V)算法的资料,真得好少呀,关于“单调队列”那部分算法,又没说明得很清楚,看了几遍没看懂原理,只好自己动脑去想怎么实现O(N*V)算法。 若用F[i][j]表示对容量为j的背包,处理完前i种物品后,背包内物品可达到的最大总价值,并记m[i] = min(n[i], j / v[i])。放入背包的第i种物品的数目可以是:0、1、2……,可得: F[i][j] = max { F[i - 1] [j – k * v[i] ] + k * w[i] } (0 <= k <= m[i]) ㈠ 如何在O(1)时间内求出F[i][j]呢? 先看一个例子:取m[i] = 2, v[i] = v, w[i] = w, V > 9 * v, 并假设 f(j) = F[i - 1][j],观察公式右边要求最大值的几项: j = 6*v: f(6*v)、f(5*v)+w、f(4*v)+2*w 这三个中的最大值 j = 5*v: f(5*v)、f(4*v)+w、f(3*v)+2*w 这三个中的最大值 j = 4*v: f(4*v)、f(3*v)+w、f(2*v)+2*w 这三个中的最大值 显然,公式㈠右边求最大值的几项随j值改变而改变,但如果将j = 6*v时,每项减去6*w,j=5*v时,每项减去5*w,j=4*v时,每项减去4*w,就得到: j = 6*v: f(6*v)-6*w、f(5*v)-5*w、f(4*v)-4*w 这三个中的最大值 j = 5*v: f(5*v)-5*w、f(4*v)-4*w、f(3*v)-3*w 这三个中的最大值 j = 4*v: f(4*v)-4*w、f(3*v)-3*w、f(2*v)-2*w 这三个中的最大值 很明显,要求最大值的那些项,有很多重复。

队列的定义及基本操作

附件2: 北京理工大学珠海学院实验报告 ZHUHAI CAMPAUS OF BEIJING INSTITUTE OF TECHNOLOGY 实验题目队列的定义及基本操作实验时间 一、实验目的、意义 (1)掌握对列的定义和基本操作,熟练掌握循环队列的操作及应用, 掌握循环队列的入队和出队等基本操作。 (2)加深对队列结构的理解,逐步培养解决实际问题的编程能力 二、实验内容及要求 说明1:学生在上机实验时,需要自己设计出所涉及到的函数,同时设计多组输入数据并编写主程序分别调用这些函数,调试程序并对相应的输出作出分析;修改输入数据,预期输出并验证输出的结果,加深对有关算法的理解。 具体要求: 定义循环队列,完成队列的基本操作:入队和出队等。(参见教材59页)三、实验所涉及的知识点 队列的操作特点是“先进先出”。前者主要是头指针、尾指针的使用,后者主要是理解循环队列提出的原因及其特点。两者都要掌握队列空与满的判定条件以及出队列、入队列操作的实现。 四、实验记录 数据结构的算法一般需修改才能在C环境下运行,如算法中采用了C++语言的引用参数,需作修改:将C++函数形参表中以&打头的参数改成以*打头的参数,再在函数体该参数前加*即可。 五、实验结果及分析 主菜单

入队 打印输出当前队列 打印输出当前队列头元素 出队

六、总结与体会 在这次实验中,我没有在上机课的时间内按时完成,我从中发现了自己有许多的不足之处,很多内容老师在上课的时候讲过,没有认真听,所以编的程序经常出差。碰到问题就参考百度或问同学,没有自己的主见。C语言的知识还不够熟练,平时缺少练习。其实还有许多不足之处,这次实验让我更意识到了学习的重要性,不能学了新科目的知识就忘了以前学过的科目的知识。 七、程序清单(包含注释) #include #include #define true 1 #define false 0 #define Size 50 //队列的数据结构 typedef struct { int element[Size];//队列的元素空间 int front;//头指针指示器 int rear;//尾指针指示器 int count;//计数器,记录队中元素总数 }SeqQueue; //函数声明表 void Print(SeqQueue *Q); void Choose(int choice,SeqQueue *Q); void InitQueue(SeqQueue *Q); int EnterQueue(SeqQueue *Q,int x); int DeleteQueue(SeqQueue *Q,int *x); void PrintQueue(SeqQueue *Q); int QueueEmpty(SeqQueue *Q);