差分——括号匹配的应用

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

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;
begin
readln(n);//有n组数据
for j:=1 to n do
begin
  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.
posted @ 2016-09-29 12:28  JRX2015U43  阅读(141)  评论(0编辑  收藏  举报