差分——括号匹配的应用

来源:互联网 发布:excel抓取多页网页数据 编辑:程序博客网 时间:2024/06/09 16:26

给出一堆括号让你判断其能否匹配,这应该是一道很水的模拟进出栈的题。现在给出模板代码:

var  s:ansistring;  i,j,top,n:longint;  flag:boolean;  a:array[0..1000001]of longint;function fct(x:char):longint;begin  case x of      '<':exit(1);      '>':exit(1);        '(':exit(2);        ')':exit(2);        '[':exit(3);        ']':exit(3);        '{':exit(4);        '}':exit(4);    end;end;beginreadln(n);//有n组数据for j:=1 to n dobegin  a[0]:=5;  flag:=true;  readln(s);//读入的括号序列  top:=0;  for i:=1 to length(s) do  begin        if s[i]='<' then        begin          inc(top);a[top]:=fct('<');            if a[top]>a[top-1] then flag:=false;        end;    if s[i]='(' then        begin          inc(top);a[top]:=fct('(');            if a[top]>a[top-1] then flag:=false;        end;    if s[i]='[' then        begin          inc(top);a[top]:=fct('[');            if a[top]>a[top-1] then flag:=false;        end;        if s[i]='{' then        begin          inc(top);a[top]:=fct('{');            if a[top]>a[top-1] then flag:=false;        end;//都是左括号,进栈    if (s[i]='>')or(s[i]=')')or(s[i]=']')or(s[i]='}') then      if a[top]=fct(s[i]) then dec(top) else flag:=false;//右括号出栈    if top<0 then flag:=false;        if not flag then break;  end;  if top<>0 then flag:=false;  if flag then writeln('YES') else writeln('NO');end;end.

在完全理解上面的代码之后,你就可以向下看了。
PS:其实下面的内容和上面并没有什么关系。。。

序列统计
【题目描述】
一个有N个数的数列A(初始时全部为0),并给出M个操作,每个操作给出两个整数l和r,把[l,r]里的数全部加1,最后输出这个序列。
1<=N<=1000000
1<=M<=1000000
【分析】
根据数据范围就可以看出,按照题目的描述去模拟的方法显然会超时。于是我们选用一种叫做差分的技巧(其实还有很多其他的方法,不过这种方法的代码应该最短)求解。如果把此题看成括号匹配,就是说l是左括号,r是右括号,然后打标记——在A[l]上打上+1,在A[r+1]上打上-1。然后一个循环扫描一遍,记录当前经过的标签之和(+1就是加1,-1就是减1)就可以了。
光是语言描述可能不太清楚,下面举个例子:有5个数,操作给出l=2,r=4。
初始:0 0 0 0 0
标记:0 1 0 0 -1
扫描:0 1 1 1 0
看,是不是A[2]~A[4]都变成了1!
这样对于每个操作只要O(1)的时间打标记,最后一重循环统计即可,总时间复杂度O(M+N)。

矩阵统计
【题目描述】
给出一个N*N的矩阵A,初始时全部为0。并给出M个操作,每个操作给出四个整数x1,y1,x2,y2,表示把A[x1,y1]~A[x2,y2]中的所有数都加上1。最后输出整个矩阵。
1<=N<=1000
1<=M<=1000000
【分析】
就是比上一题增加了一个维度,仍然可以用一维的方法来做,不过打标记的方法变了——在A[x1..x2,y1]中打上+1,在A[x1..x2,y2+1]中打上-1,最后的每一行都按照一维方法操作即可。

#include<cstdio>int f[1010][1010]={0},sum[1010][1010];int main(){    int n,m,x1,y1,x2,y2;    scanf("%d%d",&n,&m);    for (int i=1;i<=m;i++) {        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);        for (int j=x1;j<=x2;j++) { f[j][y1]+=1;f[j][y2+1]-=1; }    }    for (int i=1;i<=n;i++) {      int t=0;        for (int j=1;j<=n;j++) {            t+=f[i][j];            sum[i][j]=t;        }    }    for (int i=1;i<=n;i++) {        for (int j=1;j<=n;j++) printf("%d ",sum[i][j]);        printf("\n");    }    return 0;}

校门外的树
【题目描述】
某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置;数轴上的每个整数点,即0,1,2,……,L,都种有一棵树。
由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。
1<=L<=1000000
1<=区域数量<=1000000
【分析】
用数组模拟的方法自然是过不了的,于是我们想到了差分。注意上面的算法都是把某个区间加1,而此题是减1。而且最后输出的不是马路的情况,而是树的数量,所以在最后扫描的时候要略作改动。

var  i,n,m,x,y:longint;    f:array[0..100000001]of shortint;begin  readln(n,m);  fillchar(f,sizeof(f),0);  for i:=1 to m do begin    readln(x,y);    inc(f[x]);dec(f[y+1]);  end;  x:=0; y:=0;  for i:=0 to n do begin    inc(x,f[i]);    if x=0 then inc(y);  end;  writeln(y);end.
3 0