1. 今天晚自习机房刷题,有一道题最终WA掉两组,极其不爽,晚上回家补完作业欣然搞定它,特意来写篇博文来记录下

(最想吐槽的是这个叫做分治的分类,里面的题目真的需要分治吗。。。)

先来说下分治法

分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。

分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。

三、分治法适用的情况

分治法所能解决的问题一般具有以下几个特征:

1) 该问题的规模缩小到一定的程度就可以容易地解决

2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。

3) 利用该问题分解出的子问题的解可以合并为该问题的解;

4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。

第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;

第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;、

第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。

第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。

依据分治法设计程序时的思维过程

实际上就是类似于数学归纳法,找到解决本问题的求解方程公式,然后根据方程公式设计递归程序。

1、一定是先找到最小问题规模时的求解方法

2、然后考虑随着问题规模增大时的求解方法

3、找到求解的递归函数式后(各种规模或因子),设计递归程序即可。

网上和书上学了下,然而你丫做题基本用不大着,也是令我非常无奈

下面是题目。。。。

一、输出前k大的数

7617:输出前k大的数

总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 65536kB

描述

给定一个数组,统计前k大的数并且把这k个数从大到小输出。

输入

第一行包含一个整数n,表示数组的大小。n < 100000。

第二行包含n个整数,表示数组的元素,整数之间以一个空格分开。每个整数的绝对值不超过100000000。

第三行包含一个整数k。k < n。

输出

从大到小输出前k大的数,每个数一行。

样例输入

10

4 5 6 9 8 7 1 2 3 0

5

样例输出

9

8

7

6

5

然而此题略蛋疼,表示C++党一个sort轻松+愉快

于是就有代码。。。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6. int a[100000]={0};
  7. int main()
  8. {
  9. int n,m;
  10. cin>>n;
  11. for (int i=1;i<=n;i++)
  12. scanf("%d",&a[i]);
  13. sort(a+1,a+n+1);
  14. cin>>m;
  15. for (int i=n;i>=n-m+1;i--)
  16. cout<<a[i]<<endl;
  17. //printf("%d ",&a[i]);
  18. return 0;
  19. }

此题不解释

二、区间合并

这个题好像排序也解决了。。。表示我不禁想吐槽了

代码(这个题一开始智商被压制了。。。有点略蠢)

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6. struct data{
  7. int head,tail;
  8. };
  9. data qj[50010]={0};
  10. int cmp(data x,data y)
  11. {
  12. if (x.head<y.head)
  13. return 1;
  14. else
  15. if (x.head==y.head)
  16. if (x.tail<=y.tail)
  17. return 1;
  18. else
  19. return 0;
  20. else
  21. return 0;
  22. }
  23. int main()
  24. {
  25. int n;
  26. bool f=false;
  27. int start=0,end=0;//如果可以合并,开头和结尾
  28. scanf("%d",&n);
  29. int i,j;
  30. for (i=1;i<=n;i++)
  31. {
  32. scanf("%d%d",&qj[i].head,&qj[i].tail);
  33. end=max(end,qj[i].tail);//表示结尾的取所有区间的结尾的最大值
  34. }
  35. sort(qj+1,qj+n+1,cmp);//按照区间的开头进行排序,从小到大,一样则结尾从小到大排序
  36. start=qj[1].head;//表示开头的为最小的那个
  37. int h=qj[1].head,t=qj[1].tail;//为下文判断是否可以合并做准备
  38. for (i=2;i<=n;i++)
  39. if (qj[i].head>t)//因为有序,满足这个就不能合并
  40. {f=true;break;}
  41. else
  42. t=max(t,qj[i].tail); //两个合并后,把当前最大的结尾记录,为以后对比做铺垫
  43. if (!f)
  44. printf("%d %d",start,end);
  45. else
  46. printf("no");
  47. return 0;
  48. }

三、求排列的逆序数

逆序数,及排列中的逆序对的个数,此处用的归并排序,对归并排序进行一个计数的操作即可实现(终于正常的用了次分治,着实不易啊)

7622:求排列的逆序数

总时间限制: 1000ms 内存限制: 65536kB

描述

在Internet上的搜索引擎经常需要对信息进行比较,比如可以通过某个人对一些事物的排名来估计他(或她)对各种不同信息的兴趣,从而实现个性化的服务。

对于不同的排名结果可以用逆序来评价它们之间的差异。考虑1,2,…,n的排列i1,i2,…,in,如果其中存在j,k,满足 j < k 且 ij > ik, 那么就称(ij,ik)是这个排列的一个逆序。

一个排列含有逆序的个数称为这个排列的逆序数。例如排列 263451 含有8个逆序(2,1),(6,3),(6,4),(6,5),(6,1),(3,1),(4,1),(5,1),因此该排列的逆序数就是8。显然,由1,2,…,n 构成的所有n!个排列中,最小的逆序数是0,对应的排列就是1,2,…,n;最大的逆序数是n(n-1)/2,对应的排列就是n,(n-1),…,2,1。逆序数越大的排列与原始排列的差异度就越大。

现给定1,2,…,n的一个排列,求它的逆序数。

输入

第一行是一个整数n,表示该排列有n个数(n <= 100000)。

第二行是n个不同的正整数,之间以空格隔开,表示该排列。

输出

输出该排列的逆序数。

样例输入

6

2 6 3 4 5 1

样例输出

8

提示

1. 利用二分归并排序算法(分治);

2. 注意结果可能超过int的范围,需要用long long存储。

此处稍提一下归并排序

归并排序的基本思想

将待排序序列R[0…n-1]看成是n个长度为1的有序序列,将相邻的有序表成对归并,得到n/2个长度为2的有序表;将这些有序序列再次归并,得到n/4个长度为4的有序序列;如此反复进行下去,最后得到一个长度为n的有序序列。

综上可知:

归并排序其实要做两件事:

(1)“分解”——将序列每次折半划分。

(2)“合并”——将划分后的序列段两两合并后排序。

这道题在归并时记一个数就可以,具体的可以证明,但就不多提了

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. using namespace std;
  4. int a[100010]={0},L[100010]={0},R[100010]={0};
  5. long long ans=0;
  6. #define M 1000000000;
  7. void gb(int left,int mid,int right)
  8. {
  9. int ll=mid-left+1;
  10. int lr=right-mid;
  11. for (int i=1;i<=ll;i++)
  12. L[i]=a[left+i-1];//此操作方便处理
  13. for (int i=1;i<=lr;i++)
  14. R[i]=a[mid+i];
  15. L[ll+1]=M;//将最后一个赋给一个极大值,可以当作一个“哨兵”非常的精妙,然而经测试似乎不加这条语句会出错
  16. R[lr+1]=M;
  17. int l=1,r=1;
  18. for (int i=left; i<=right; i++)
  19. {
  20. if (L[l]<=R[r])
  21. {
  22. a[i]=L[l];
  23. l++;
  24. }
  25. else
  26. {
  27. a[i]=R[r];
  28. r++;
  29. ans+=ll-l+1;//求逆序对的唯一多出来的语句
  30. }
  31. }
  32. }
  33. void gbsort(int left,int right)
  34. {
  35. int mid=(left+right)/2;
  36. if (left<right)
  37. {
  38. gbsort(left,mid);//不断分治
  39. gbsort(mid+1,right);
  40. gb(left,mid,right);
  41. }
  42. }
  43. int main()
  44. {
  45. int n;
  46. scanf("%d",&n);
  47. for (int i=1; i<=n; i++)
  48. scanf("%d",&a[i]);
  49. gbsort(1,n);
  50. printf("%lld",ans);
  51. return 0;
  52. }

四、一元三次方程求解

然而第一眼看到这个题其实我是拒绝的,有想过用求根公式,然而本人数学并不是非常好(当然也不是很差)表示求根公式和数学求法非常神,表示理解还不如二分查找答案多编几行代码快,于是开始二分

PS:此处鸣谢聪哥的数学指引

7891:一元三次方程求解

总时间限制: 1000ms 内存限制: 65536kB

描述

有形如:ax3+bx2+cx+d=0 这样的一个一元三次方程。

给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在-100至100之间),且根与根之差的绝对值>=1。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后2位。

输入

一行,包含四个实数a,b,c,d,相邻两个数之间用单个空格隔开。

输出

一行,包含三个实数,为该方程的三个实根,按从小到大顺序排列,相邻两个数之间用单个空格隔开,精确到小数点后2位。

样例输入

1.0 -5.0 -4.0 20.0

样例输出

-2.00 2.00 5.00

提示

记方程f(x)=0,若存在2个数x1和x2,且x1(此处题目应该是残了。。。)(请无视)

闲话不多说上代码吧,这个题也没什么好扯的。。。

  1. #include<cstdio>
  2. #include<cmath>
  3. using namespace std;
  4. double a,b,c,d;
  5. double f(double x)
  6. {
  7. return a*x*x*x+b*x*x+c*x+d;
  8. }
  9. double search(double left,double right)
  10. {
  11. double mid=(left+right)/2;
  12. if (right-left<1e-4) return mid;
  13. else
  14. {
  15. if (f(mid)==0) return mid;
  16. else if (f(left)*f(mid)<0) return search(left,mid);
  17. else return search(mid,right);
  18. }
  19. }
  20. int main()
  21. {
  22. double x1,x2;
  23. scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
  24. if (a<0)
  25. {
  26. a=-a;b=-b;c=-c;d=-d;
  27. }
  28. x1=(-2*b-sqrt(4*b*b-12*a*c))/(a*6);
  29. x2=(-2*b+sqrt(4*b*b-12*a*c))/(a*6);
  30. double ans1,ans2,ans3;
  31. ans1=search(-101,x1);
  32. ans2=search(x1,x2);
  33. ans3=search(x2,101);
  34. printf("%.2f %.2f %.2f",ans1,ans2,ans3);
  35. return 0;
  36. }

五、统计数字

第一眼看题,以为是随便一个桶排就出来了,后来一看数据范围就呵呵了,不过这个题着实还是不难,然而还没有离散化难呢,那怕个什么啊,水量仅次第一题的存在,排个序,统计个数一波输出,一波带走

然而我蛋神@tyc0520居然一开始不会编,表示莫名奇妙,先跪一发

7909:统计数字

总时间限制: 1000ms 内存限制: 65536kB

描述

某次科研调查时得到了n个自然数,每个数均不超过1500000000(1.5*109)。已知不相同的数不超过10000个,现在需要统计这些自然数各自出现的次数,并按照自然数从小到大的顺序输出统计结果。

输入

包含n+1行:

第一行是整数n,表示自然数的个数;

第2~n+1每行一个自然数。

40%的数据满足:1<=n<=1000;

80%的数据满足:1<=n<=50000;

100%的数据满足:1<=n<=200000,每个数均不超过1500 000 000(1.5*10^9)。

输出

包含m行(m为n个自然数中不相同数的个数),按照自然数从小到大的顺序输出。每行输出两个整数,分别是自然数和该数出现的次数,其间用一个空格隔开。

样例输入

8

2

4

2

4

5

100

2

100

样例输出

2 3

4 2

5 1

100 2

下面是代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6. int a[200010]={0};
  7. int main()
  8. {
  9. int n;
  10. scanf("%d",&n);
  11. for (int i=1;i<=n;i++)
  12. scanf("%d",&a[i]);
  13. sort(a+1,a+n+1);
  14. int sum=0;
  15. for (int i=1;i<=n;i++)
  16. {
  17. sum++;
  18. if (a[i]==a[i+1])
  19. continue;
  20. else
  21. {
  22. printf("%d %d\n",a[i],sum);
  23. sum=0;
  24. }
  25. }
  26. return 0;
  27. }

好了,时间不早了,洗睡去了,表示前路漫漫,仍须努力啊

NOI题库分治算法刷题记录的更多相关文章

  1. 【托业】【全真题库】TEST01-03-阅读题

    [托业][全真题库]TEST01-03-阅读题

  2. leetcode 算法刷题(一)

    今天开始刷Leetcode上面的算法题.我会更新我刷题过程中提交的代码(成功和不成功的都有)和比较好的解法 第二题 Add Two Numbers 题目的意思:输入两个链表,这两个链表都是倒序的数字, ...

  3. 2019年Amazon AWS-Solutions-Architect-Professional考试最新题库(AWS SAP题库)带考试模拟器

    大家好,由于最近自己备考Amazon AWS-Solutions-Architect-Professional考试,购买了以下链接的题库,并通过了考试 https://www.kaoguti.gq/A ...

  4. php+mysql开发一个最简单的在线题库,在线做题系统!

    题库,对于教育机构,学校,在线教育,是很有必要的,网上也有不少的第三方在线题库系统,但是本次案例,会让有需要的人了解题库的开发思路,其实很简单,无非就是一个表单验证,数据库验证. 1.先构建表单数据2 ...

  5. leetcode算法刷题(三)

    今天在刷了几道简单的动态规划后,又看了看string方面的题 第五题 Longest Palindromic Substring 题目的意思:求一个字符串的最长回文子串 分析:开始,我的想法是,现在字 ...

  6. leetcode算法刷题(四)——动态规划(二)

    又到了晚上,动态规划,开刷! 第121题 Best Time to Buy and Sell Stock 题目的意思:给予一个数组price,表示特定股票在某天的股价,里面第i个数表示第i天的价格.只 ...

  7. leetcode算法刷题(二)——动态规划(一)

    上次刷了五六道题,都是关于string处理的,这次想换个知识点刷一下,然后再回头刷string的题,当做复习.. 这几天主要会选择动态规划的题目,因为以前从没刷过这方面的东西,很多东西都不是很懂..就 ...

  8. Leetcode算法刷题:217和219题 Contains Duplicate

    从题目名字就可以看出这两道题是相似的,219是217的加强版 217:Contains Duplicate 题目 给予一个数组,判断是否有重复的元素.如果有就返回True,没有就返回False.以下是 ...

  9. leetcode算法刷题(五)——动态规划(三)

    今天的题目不是leetcode上面的.只是觉得动态规划还是不算很熟练,就接着找了点DP的题练练 最长递增子序列的长度 题目的意思:传入一个数组,要求出它的最长递增子序列的长度.例如:如在序列1,-1, ...

随机推荐

  1. PAT 1011. A+B和C (15)

    给定区间[-231, 231]内的3个整数A.B和C,请判断A+B是否大于C. 输入格式: 输入第1行给出正整数T(<=10),是测试用例的个数.随后给出T组测试用例,每组占一行,顺序给出A.B ...

  2. .NET MVC控制器分离到类库的方法

    在.ASP.NET MVC的开发中,我们创建完项目之后,ASP.NET MVC是已Model-Controller-View的形式存在的,在创建项目自动生成的内容上Model我们很容易分离成类库,所以 ...

  3. [Azure] 使用 Visual Studio 2013 管理中国版 Azure 订阅

    比较关心微软平台技术的朋友应该都知道,微软云服务(Microsoft Azure)以下简称Azure分为全球版和中国版,由于政府法规问题中国版的服务是由二十一世纪互联运营,整体来看中国版Azure和全 ...

  4. [LINK]OpenResty

    http://openresty.org/ http://www.tuicool.com/articles/M3yI3y http://www.oschina.net/question/28_6046 ...

  5. 如何用 fiddler 捕获 https 请求

    安装完 Fiddler 后,我们每次打开浏览器输入 url,Fiddler 便会捕获到我们的 http 请求(Fiddler 是以代理 web 服务器的形式工作的,它使用代理地址:127.0.0.1, ...

  6. [C#解惑] #1 在构造函数内调用虚方法

    谜题 在C#中,用virtual关键字修饰的方法(属性.事件)称为虚方法(属性.事件),表示该方法可以由派生类重写(override).虚方法是.NET中的重要概念,可以说在某种程度上,虚方法使得多态 ...

  7. 前端框架——BootStrap学习

    BootStrap简单总结下:1.栅格系统,能够很好的同时适应手机端和PC端(及传说中的响应式布局) 2.兼容性好 接下来是对BootStrap学习的一些基础案例总结和回顾: 首先引入:bootstr ...

  8. Rest API 开发 学习笔记(转)

    Rest API 开发 学习笔记 概述 REST 从资源的角度来观察整个网络,分布在各处的资源由URI确定,而客户端的应用通过URI来获取资源的表示方式.获得这些表徵致使这些应用程序转变了其状态.随着 ...

  9. android开发------第一个android程序

    好吧,现在我们就一起来写第一个android程序,看它带给了我们什么.sdk的使用和虚拟机的创建我就不说了.项目创建过程先略过,不太重要. 那第一个程序我们能学到什么知识呢?一起看吧.^-^ 在IDE ...

  10. jax-ws开发总结

    服务端开发步骤: 1.定义SEI,即java中的接口 2.定义SEI的实现类,使用@webservice注解标记它是一个webservice服务类 3.发布服务 客户端开发步骤:使用jdk的servi ...