【问题标题】:Counting line segments efficiently有效计算线段
【发布时间】:2014-07-30 05:14:37
【问题描述】:

我正在尝试解决来自澳大利亚信息学培训网站的Slicing Paradise。这个想法是您有一系列块(实际上是语句中的森林,以及我的代码中的land),并且每次删除一个块(或转换为度假村),这可能会增加或减少连续(森林)块的运行。我要计算历史上的最大运行次数。输入是块的数量 N,然后是 N 行,给出每个块的移除时间。时间是从 1 开始的连续整数。

我的代码运行良好(我认为),但对于一些较大的情况来说它太慢了。有人可以建议一个简单的快速工作解决方案,或者可以对我的进行任何改进以使其在 O(N) 中运行吗?

慢解

#include <cstdio>

FILE* infile;
FILE* outfile;
int N,c,j,l,best;

int main(){
    infile=fopen("slicein.txt","r");
    outfile=fopen("sliceout.txt","w");

    fscanf(infile,"%d\n",&N);

    int land[N+1];
    int location[N+1];

    for (int i=1;i<=N;i++){
        fscanf(infile,"%d\n",&location[i]);
        land[i]=0;
    }

    j=1;l=1;c=1;

    for (int i=1;i<=N;i++){
        if (location[i]==j){
            land[i]=1;
            if (land[i+1]!=1 && land[i-1]!=1 && i!=N && i!=1) c++;
            else if (land[i-1]==1 && land[i+1]==1 && i!=N && i!=1) c--;
            j++;
            if (c>best) best=c;
            i=0;
        }
        if (i==N && l!=N*N){
            i=0;
        }
        else if (l==N*N) break;
        l++;
    }

    fprintf(outfile,"%d\n",best);

    fclose(infile);
    fclose(outfile);

    return 0;
}

【问题讨论】:

  • 你的基准是什么,表明它“太慢了”?
  • 不仔细看你的代码,运行起来好像是O(N),那为什么会太慢呢?
  • @Andreas 在线评委说太慢了。如果您阅读问题陈述中提到的“约束”,它会说 30% 的分数 Nimgur.com/j9lw95Q o_weisman 这不是 O(N)。它是 O(N^2)。看整数 l。我只有在达到 N^2 的值时才会中断。
  • 您是否尝试过分析器来查看哪个位较慢?

标签: c++ algorithm math


【解决方案1】:

您的问题是您实际上有两个嵌套循环,一个用于j 的外部循环和一个用于i 的内部循环,尽管您没有明确地这样写。这为您提供了一种 O(N) 的方法来查找当前位置,这相当于 O(N2) 的总成本。

基本信息是在每一轮j 中,您需要找到在该轮中转换的位置i。您可以在读取输入时为此构建地图。与其将location 作为一个位置到另一个位置的映射,不如让它成为一个不同位置的映射:

for (int i=1;i<=N;i++){
    int t;
    fscanf(infile,"%d\n",&t);
    location[t] = i;
    land[i]=0;
}

然后您可以使用单个 O(N) 循环,一切都应该没问题。确保索引正确,因为您似乎在某些地方使用从 1 开始的索引,而在其他地方使用从 0 开始的索引。

【讨论】:

    【解决方案2】:

    无法找出您的解决方案,但以下是在 O(nlogn) 中执行此操作的算法:-

    1. 将元组 (i,time[i]) 存储在向量中
    2. 根据时间[i]对向量进行排序
    3. 维护一个布尔数组来检查是否有一个绘图。
    4. taken[i] = false 对于所有 i 最初
    5. 开始按排序顺序构建绘图。
    6. 最初的森林 = 1
    7. 在构建绘图时,检查布尔数组中的相邻绘图。
    8. 如果两者都被占用,则森林 = 森林 - 1
    9. 如果有人被占用则没有变化
    10. 如果两者都是空森林 = 森林 + 1
    11. 在每次构建后检查林 id 是否大于最大计数。

    时间复杂度:-

    Sorting takes O(logn)
    plot construction in sorted order O(n)
    

    【讨论】:

      【解决方案3】:

      对于任何感兴趣的人,这是我的答案:

      #include <cstdio>
      
      FILE* infile;
      FILE* outfile;
      int N,c,j,l,b,best;
      
      int main(){
      infile=fopen("slicein.txt","r");
      outfile=fopen("sliceout.txt","w");
      
      fscanf(infile,"%d\n",&N);
      
      int land[N+1];
      int location[N+1];
      j=1;l=1;c=1;b=1;
      
      for (int i=1;i<=N;i++){
          int t;
          fscanf(infile,"%d\n",&t);
          location[t]=i;
          land[i]=0;
      }
      for (int i=1;i<=N;i++){
          land[location[i]]=1;
          if (land[location[i]-1]!=1 && land[location[i]+1]!=1 && location[i]!=1 && location[i]!=N) c++;
          else if (land[location[i]-1]==1 && land[location[i]+1]==1 && location[i]!=1 && location[i]!=N) c--;
          else if (land[location[i]-1]==1 && location[i]==N) c--;
          else if (land[location[i]+1]==1 && location[i]==1) c--;
          if (c>best) best=c;
      }
      
      fprintf(outfile,"%d\n",best);
      
      fclose(infile);
      fclose(outfile);
      
      return 0;}
      

      感谢 MvG 提出算法 :)

      【讨论】:

      • 你的代码有未定义的行为:你访问land[location[i]+1]之前检查location[i]!=N,所以你可能会访问超出数组的末尾。通常这是有效的,但严格来说你不能依赖这个。但是有一种方法可以避免很多大小写区别:将land 数组的长度设为N+2,并将land[0]land[N+1] 设置为1。那么你只需要看左右值,边界没有任何特殊情况。您还应该改进缩进,也许使用一个变量来保存location[i] 的值以使内容更具可读性。
      猜你喜欢
      • 1970-01-01
      • 2018-04-09
      • 1970-01-01
      • 2017-06-01
      • 1970-01-01
      • 2018-11-08
      • 1970-01-01
      • 1970-01-01
      • 2017-12-23
      相关资源
      最近更新 更多