先来介绍一下时间复杂度:

同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法。

计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。这是一个关于代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,它考察当输入值大小趋近无穷时的情况。


时间复杂度的优化通常在暴力枚举中尤为重要,或许O(n*n)会得零分但是O(n*logn)可以得满分,所以在编写程序过程中我们要优先考虑时间较短的算法(洛谷里最绝望的就是看到TLE,这意味着代码要重新编写)。总之有快方法用上绝对没问题,除非NOIP时间不够可以快速写一段骗(拿)一些分数。



下面以洛谷P2241 统计方形(数据加强版)为例讲解一下具体如何不断优化程序的时间复杂度:

例10.1 (洛谷 P2241, NOIP1997 普及组 加强)

有一个 n×m(n, m ≤ 5000) 的棋盘,求其方格包含多少个(四边平 行于坐标轴的)正方形和长方形。

本题中, 长方形中不包括正方形。

样例解释: 如图,正方形一共 8 个,长方形 10 个 正方形中,边长为 1 的 6 个,边长为 2 的 2 个;

     长方形中, 1 ×2 的 4 个, 1 ×3 的 2 个, 2 × 1 的 3 个, 2 ×3 的 1个。


思路1:用四重循环,直接枚举 4 个参数,即两横边两竖边:

通过左上角和右下角的顶点进行枚举,如果长度相等就是正方形,否则就是长方形。

所以,这样可以保证不重复地遍历所有的方形。

根据循环的范围可知,我们也没有遗漏任何的方形。

时间复杂度O(n2 m2 ) ……似乎有点慢。

但是至少, 答案正确了。

  1. 1 //枚举左上、右下顶点 时间复杂度O(n^2*m^2)
  2. 2 #include<bits/stdc++.h>
  3. 3 using namespace std;
  4. 4 typedef long long LL; //LL是已经定义好的long long
  5. 5 int main(){
  6. 6 int n,m;
  7. 7 LL squ=0,rec=0;//squ统计正方形个数,rec统计长方形个数
  8. 8 cin>>n>>m;
  9. 9 for(int x1=0;x1<n;x1++)
  10. 10 for(int y1=0;y1<m;y1++)
  11. 11 for(int x2=x1+1;x2<=n;x2++)
  12. 12 for(int y2=y1+1;y2<=m;y2++) //四层循环不TLE才怪
  13. 13 if (x2-x1 == y2-y1)squ++;
  14. 14 else rec++;
  15. 15 cout<<squ<<" "<<rec<<endl;
  16. 16 return 0;
  17. 17 }

                

为什么是 x2 从 x1+1 开始, y2 从 y1+1 开始枚举?

• 如果 x1 > x2, 那么 x1 就不再是左侧了, x2 才是左侧 (左图)

• 如果 x1 = x2, 那么无法构成长方形, 退化为一根线段 (中图)(就是那根消失的线段)

• 只有 x1 < x2, 才能正常构成长方形(右图)。

y1 、y2 同理



思路 2: 以下左图中的点 (3, 4)为例(左下角为原点)。

位于同一对角线 (图中虚线)上所有点均可构成正方形。

除自身所在行列外,所有其它点均可与其构成长方形;故直接得 长方形数为 nm – 正方形数。

从右图可以看出, 每一个长/正方形均被其 4 个顶点各计算一次。 因此,最终答案需要除以 4。

斜线上的格点个数为多少呢?

若顶点在长方形顶点,格点个数为长方形的短边长,即 min(n,m)。

否则,以顶点为界分为4份 。

每份都这样计算,得到正方形个数: min x, y + min n − x, y + min x, m − y + min(n − x, m − y)

因此, 枚举(x, y)后,可在 O(1) 时间内计算答案,求和。 总时间复杂度 O(nm),直接优化掉一个 O(nm)

  1. 1 //枚举一个点构成的所有矩形,统计结果除4 时间复杂度O(n*m)
  2. 2 #include<bits/stdc++.h>
  3. 3 using namespace std;
  4. 4 typedef long long LL; //LL是已经定义好的long long
  5. 5 int main(){
  6. 6 int n,m;
  7. 7 LL squ=0,rec=0;
  8. 8 cin>>n>>m;
  9. 9 for(int x=0;x<=n;x++)
  10. 10 for(int y=0;y<=m;y++){
  11. 11 LL tmp=min(x,y)+min(n-x,y)+min(x,m-y)+min(n-x,m-y);//对角线的正方形枚举方式
  12. 12 squ+=tmp;
  13. 13 rec+=n*m-tmp;
  14. 14 }
  15. 15 cout<<squ/4<<" "<<rec/4<<endl;//四个顶点算了四次,所以要除4输出
  16. 16 return 0;
  17. 17 }


还能不能更快呢?

思路 3:每一个长方形重复了4次。能否不重复呢?

结合思路 1可知, 只需考虑右上角为 (x, y) 的情况,因此计算斜线 上的顶点时, 只需要向左下角一个方向拓展!

(先算 4 个方向, 再除以 4,可谓是画蛇添足啊…… )

  1. 1 //枚举右下角顶点 时间复杂度O(n*m)
  2. 2 #include<bits/stdc++.h>
  3. 3 using namespace std;
  4. 4 typedef long long LL;
  5. 5 int main(){
  6. 6 int n,m;
  7. 7 LL squ=0,rec=0;
  8. 8 cin>>n>>m;
  9. 9 for(int x=0;x<=n;x++)
  10. 10 for(int y=0;y<=m;y++){
  11. 11 LL tmp=min(x,y);
  12. 12 squ+=tmp;
  13. 13 rec+=x*y-tmp;
  14. 14 }
  15. 15 cout<<squ<<" "<<rec<<endl;
  16. 16 return 0;
  17. 17 }

当然,这里选择固定其它角也是等价的,但是固定右上角最简单。

注意:这里的原点是在左下角, 列是 x,行是 y。

如果选择左上角 作为原点, 那么枚举的就是长方形右下角。



思路4:枚举边长 (a, b)。题目变为在 n × m 的长方形中能放置多少 个 a×b 的方形。

注意 a 、b 有序, a×b 和 b×a 不等价。

• n 列中选连续 a 列:[1,a],[2,a+1], … ,[n-a+1,n],共 n-a+1 种可能。

• m 行中选连续 b 行构成方形, 同理有 m-b+1 种情况。 所以 n × m 的长方形中可容纳 (n-a+1)×(m-b+1) 个 a×b 的矩形。

对于边长 k, 只有长等于宽, 才能构成正方形;其余均为长方形。

如果 a=b, 就计入正方形。 使用循环枚举 a 、b, 累加求和即可

  1. 1 //枚举两条边 时间复杂度O(n*m)
  2. 2 #include<bits/stdc++.h>
  3. 3 using namespace std;
  4. 4 typedef long long LL;
  5. 5 int main(){
  6. 6 int n,m;
  7. 7 LL squ=0,rec=0;
  8. 8 cin>>n>>m;
  9. 9 for(int a=1;a<=n;a++)
  10. 10 for(int b=1;b<=m;b++)
  11. 11 if(a==b)
  12. 12 squ+=(n-a+1)*(m-b+1);
  13. 13 else
  14. 14 rec+=(n-a+1)*(m-b+1);
  15. 15 cout<<squ<<" "<<rec<<endl;
  16. 16 return 0;
  17. 17 }


还可以再快!!!

思路 5:沿用刚才乘法原理的思想,枚举量还可进一步减少?

在 n × m 的长方形中的矩形个数, 等价于在 m+1 条行线中选首尾 2 行、在 n+1 条列线中选首尾 2 列所围成的方形数目!

           

在 n+1 列中选 2 列线围长方形,左列 n+1 种情况 ;右线列不能和 左列线重复, 只有 n 种情况,将他们乘起来。

由于重复统计 (左右和右左),要除以2,有 1/2n(n + 1) 种可能。 同理,行有 1/2m(m + 1) 种可能。一共1/4 m(m + 1) n (n+1)矩形。

借助思路4, 去掉其中的正方形。时间复杂度降到 O(min(m, n))。

  1. 1 //枚举一条边 时间复杂度:O(n)
  2. 2 #include<bits/stdc++.h>
  3. 3 using namespace std;
  4. 4 typedef long long LL;
  5. 5 int main(){
  6. 6 int n,m;
  7. 7 LL squ=0,rec=0;
  8. 8 cin>>n>>m;
  9. 9 for(int a=1;a<=min(n,m);a++)
  10. 10 squ+=(n-a+1)*(m-a+1);
  11. 11 rec=(n+1)*n*(m+1)*m/4-squ;
  12. 12 cout<<squ<<" "<<rec<<endl;
  13. 13 return 0;
  14. 14 }

写出这样的代码还需要担心不能AC吗?

完结撒花!!!

码字不易,点个赞再走吧。

c++代码实现中时间复杂度的不断优化的更多相关文章

  1. oracle中sql语句的优化

    oracle中sql语句的优化 一.执行顺序及优化细则 1.表名顺序优化 (1) 基础表放下面,当两表进行关联时数据量少的表的表名放右边表或视图: Student_info   (30000条数据)D ...

  2. JavaScript中的尾调用优化

    文章来源自:http://www.zhufengpeixun.com/qianduanjishuziliao/javaScriptzhuanti/2017-08-08/768.html JavaScr ...

  3. Unity中的GC以及优化

    [简介] 常见的 Unity GC 知识点总结出来的思维导图 Unity 官方文档,正巧在博客园发现了已经有位大神(zblade)把原文翻译出来了,而且质量很高~,译文地址 在这里.下面我就可耻地把译 ...

  4. C#7.2——编写安全高效的C#代码 c# 中模拟一个模式匹配及匹配值抽取 走进 LINQ 的世界 移除Excel工作表密码保护小工具含C#源代码 腾讯QQ会员中心g_tk32算法【C#版】

    C#7.2——编写安全高效的C#代码 2018-11-07 18:59 by 沉睡的木木夕, 123 阅读, 0 评论, 收藏, 编辑 原文地址:https://docs.microsoft.com/ ...

  5. vue中关于v-for性能优化---track-by属性

    vue中关于v-for性能优化---track-by属性 最近看了一些react,angular,Vue三者的对比文章,对比来说Vue比较突出的是轻量级与易上手. 对比Vue与angular,Vue有 ...

  6. MySQL 并行复制演进及 MySQL 8.0 中基于 WriteSet 的优化

    MySQL 8.0 可以说是MySQL发展历史上里程碑式的一个版本,包括了多个重大更新,目前 Generally Available 版本已经已经发布,正式版本即将发布,在此将介绍8.0版本中引入的一 ...

  7. OpenCV中的SVM参数优化

    OpenCV中的SVM参数优化 svm参数优化opencv SVMSVR参数优化CvSVMopencv CvSVM        SVM(支持向量机)是机器学习算法里用得最多的一种算法.SVM最常用的 ...

  8. shellcode在栈溢出中的利用与优化

    0x00 前言 在<Windows Shellcode学习笔记——shellcode的提取与测试>中介绍了如何对shellcode作初步优化,动态获取Windows API地址并调用,并通 ...

  9. 浅谈Unity中的GC以及优化

    介绍: 在游戏运行的时候,数据主要存储在内存中,当游戏的数据不在需要的时候,存储当前数据的内存就可以被回收再次使用.内存垃圾是指当前废弃数据所占用的内存,垃圾回收(GC)是指将废弃的内存重新回收再次使 ...

  10. JDK1.7中HashMap死环问题及JDK1.8中对HashMap的优化源码详解

    一.JDK1.7中HashMap扩容死锁问题 我们首先来看一下JDK1.7中put方法的源码 我们打开addEntry方法如下,它会判断数组当前容量是否已经超过的阈值,例如假设当前的数组容量是16,加 ...

随机推荐

  1. 在Rocky8中安装VMware Workstation 的方法

    在Rocky8中安装VMware Workstation 的方法 1.Rocky必须是图形界面 2.下载wmware workstation(下载地址:https://www.vmware.com/i ...

  2. Scrapy 发送Request Payload

    Scrapy 发送Request Payload 首先要打开 F12 进入调试模式 然后 查看是用什么方法获取的 如果是Json: 1. json.dumps 转化成Json yield Reques ...

  3. springcloud组件梳理之Feign

    最近刚好打算做一个springcloud系列的分享,趁此机会刚好梳理下springcloud常用组件的使用,今天先对feign做个简单介绍! feign是一个声明式的Web服务客户端,它使得发送web ...

  4. 使用idea创建第一个Mybatis程序及可能遇到的问题

    第一个Mybatis程序 思路:搭建环境->导入Mybatis->编写代码->执行 搭建环境 创建数据库 CREATE DATABASE `mybatis` USE `mybatis ...

  5. 一、Redis的Java客户端

    模糊的目标,要不断去解释它们,把他们转化成一个更具体的内容,这样才能够找到途径. 常用客户端介绍 Jedis客户端 基本使用(直连) 引入对应依赖 <dependency> <gro ...

  6. 强软弱引用,ThreadLocal和内存泄漏

    强引用 写法:Object obj=new Object() 引用强度:最强 只要被引用着,就不会被gc(垃圾回收)回收掉. 软引用 写法:SoftReference<String> sr ...

  7. 2022春每日一题:Day 40

    题目:[NOI2010] 超级钢琴 前求出美妙值的前缀和,然后倍增处理一下前缀和的最大值,然后对于一个左端点s,他能取到右端点的只有s+l到s+r,而他的最大贡献就是s+l 到s+r的最大子段和,因此 ...

  8. kubeEdge的MetaManager模块简介

    MetaManager 是edged和edgehub之间的消息处理器,它还负责将元数据存储到轻量级数据库SQLite或从中检索元数据(metadata). 根据以下不同的operation接收不同类型 ...

  9. Java 中九种 Map 的遍历方式,你一般用的是哪种呢?

    日常工作中 Map 绝对是我们 Java 程序员高频使用的一种数据结构,那 Map 都有哪些遍历方式呢?这篇文章阿粉就带大家看一下,看看你经常使用的是哪一种. 通过 entrySet 来遍历 1.通过 ...

  10. JVM面试点汇总

    JVM面试点汇总 我们会在这里介绍我所涉及到的JVM相关的面试点内容,本篇内容持续更新 我们会介绍下述JVM的相关面试点: JVM内存结构 内存溢出问题 方法区与永久代和元空间 JVM内存参数 JVM ...