抽签问题的思考(二分算法)

问题:
将写有数字的N个纸片放入口袋中,你可以从口袋中抽取4次纸片,每次记下纸片上的数字后都将其放回口袋中.如果这4个数字的和是M,就是你赢,否则就是你的朋友赢.

很容易就可以写出下边的代码:

 1 for(int a=0; a<n; a++){
 2     for(int b=0; b<n; b++){
 3         for(int c=0; c<n; c++){
 4             for(int d=0; d<n; d++){
 5                 if(k[a] + k[b] + k[c] + k[d]==m){
 6                     f = true;
 7                 }
 8             }
 9         }
10     }
11 }

上面是最初所记载的程序的循环部分.最内侧关于d的循环所做的事就是:

检查是否有d使得Ka+Kb+Kc+Kd=M

通过对式子移向,就能得到另一种表达方式:

检查是否有d使得Ka=M-Kb+Kc+Kd

就是检查数组K中所有元素,判断是否有M-Kb+Kc+Kd.

这样的快速查找,就要用到二分搜索的算法.

1.二分搜索与O(n3logn)

排序O(nlogn)时间

循环O(n3logn)时间

 1 int n,m,k[MAX];
 2
 3 bool binary_search(int x){
 4     //x的存在范围是k[l],k[l+l]...k[r-l]
 5     int l=0,r=n;
 6     //反复操作直到存在范围为空
 7     while(r-l>=l){
 8         int i=(l+r)/2;
 9         if(k[i]==x) return true;//找到x
10         else if(k[i]<x)   l=i+1;
11         else r=i;
12     }
13     //没找到x
14     return false;
15 }
16
17 viod solve(){
18     //为了找到二分查找需要先排序
19     sort(k,k+n);
20
21     bool f=false;
22     for(int a=0; a<n; a++){
23         for(int b=0; b<n; b++){
24             for(int c=0;c<n; c++){
25                 //将最内则的循环替换成二分查找
26                 if(binary_search(m-k[a]-k[b]-k[c])){
27                     f=true;
28                 }
29             }
30         }
31     }
32     if(f)   puts("Yes");
33     else   puts("No");
34 }

排序O(n2logn)时间

循环O(n2logn)时间

 1 int n,m,k[MAX];
 2 int kk[MAX*MAX];
 3
 4 bool binary_search(int x){
 5     //x的存在范围是kk[l],kk[l+l]...kk[r-l]
 6     int l=0,r=n*n;
 7     //反复操作直到存在范围为空
 8     while(r-l>=l){
 9         int i=(l+r)/2;
10         if(kk[i]==x) return true;//找到x
11         else if(kk[i]<x)   l=i+1;
12         else r=i;
13     }
14     //没找到x
15     return false;
16 }
17
18 viod solve(){
19     //枚举k[c]+k[d]的和
20     for(int c=0; c<n; c++){
21         for(int d=0; d<n; d++){
22             kk[c*n+d]=k[c]+k[d];
23         }
24     }
25     //排序以便进行二分搜索
26     sort(kk,kk+n*n);
27
28     bool f=false;
29     for(int a=0; a<n; a++){
30         for(int b=0; b<n; b++){
31             //将内则的两个循环替换成二分搜索
32             if(binary_search(m-k[a]-k[b])){
33                 f=true;
34             }
35         }
36     }
37     if(f)   puts("Yes");
38     else   puts("No");
39 }

本问题既需要二分搜索这一基础算法知识,也需要将四个数分成两两考虑的想象力.像这样从复杂度较高的算法出发,不断降低复杂度知道满足问题要求的过程,也是设计算法时常会经历的过程.

来自:<<挑战程序设计竞赛>>

时间: 2024-12-20 09:57:12

抽签问题的思考(二分算法)的相关文章

二分算法的一些思考

二分算法的思想: 通过不断减小问题规模,从边界条件出发求解问题.(通常是单调性问题,判定形式较为简单) 二分算法的优点: 1.把n的时间复杂度优化到logn 2.将一个问题转化为判定性质问题求解 代码: while(l<=r) { if(check(mid) { ans = mid; r = mid-1; } else ans = mid+1; } 例题: block towers n个人选择n个不同2的倍数的数,m个人选择m个3的倍数的数,选择的最大数字最小!

递归和二分算法

递归程序调用自身的编程方法称为递归(recursion) 它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量.递归的能力在于用有限的语句来定义对象的无线的集合.一般来说,递归需要有边界条件.递归前进段和递归返回段.当边界条件不满足时,递归前进:当边界条件满足时,递归返回. 递归的两个条件: 1,每一次的调用都会使问题的规模有所减少 2,必须有一个明确的终止条件 python中测试的递归

二分算法模板

二分算法求最值,掌握了算法的本质和模板,主要就是答案验证过程,验证过程经常使用贪心算法. 关键是根据题目要求设立命题check(x). 验证答案mid: Check(mid):是否成立? 求满足条件Check(mid)的最小值: –if  check(mid) then r:=mid else l:=mid+1; 求满足条件Check(mid)的最大值: –if  check(mid) then l:=mid else r:=mid-1; 模板一:求满足条件check(mid)的最小值. 1 /

二分算法和三分算法

二分算法: hdu1969    PIE 题意:f+1个人分n分pie,pie只能切开不能从组,也就是说每个人拿到的必须是一个整块,不能是多个碎块.要求每个人分的的大小一样.n份pie的大小不同.目标是求出没人可能吃到的最大大小V. 分析抽象:首先条件是必须够n个人吃,要求大小一样的情况下求最大的V.关系是随着V的增大,所能support的人越少.这里有一个非递增的关系.所以而已考虑二分法.问题对精度的要求是二分法使用的关键.要求保留4位小数,所以我们统一乘上1000000来计算. #inclu

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

最近公共祖先LCA(Tarjan算法)的思考和算法实现 LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下! //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!! 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先

what&#39; the python之递归函数与二分算法

what's the 递归? 递归函数的定义:在函数里可以再调用函数,如果这个调用的函数是函数本身,那么就形成了一个递归函数. 递归的最大深度为997,这个是程序强制定义的,997完全可以满足一般情况下用到递归的情形. #最大997层 def foo(n): print(n) n += 1 foo(n) foo(1) 举个栗子,假设你想知道A的年龄,但你只知道A比B大2岁,B又比C大两岁,C又比D大两岁,D比E大两岁,恰好你知道E的岁数,那是不是就能知道A的岁数了呢,这就可以组成一个递归.那我们

对二分算法的理解及结对编程情况

一.对二分算法理解 二分算法,又称折半算法,是应用分治策略的典型例子.二分查找主要对有序序列进行对所指定数字的查找,通过不断缩小搜查范围,在比较中间的数后对左右两个数组进行相同操作,以得到最终的带查找数字.时间复杂度logn,对数组较大时能显著提高程序效率. 二.算法代码 #include <iostream>using namespace std;int main(){ int n,x; cin >> n; if(1<=n<=100){  int a[n];  for

递归/匿名函数/三元表达式/列表生成式/字典生成式/二分算法

让在下揭开尔等的面纱,一探究竟:) >>>递归: 递归是一个往复的过程,也就是由两个过程组成,一个是计算过程,一个是将值层层返回的过程,递归的奇妙之处,就在于自身调用自身,然后,过程遵循由复杂到简单,最终满足相应条件后,退出,返回结果.说了一大堆,不过直接上代码: test_list = [1,[2,[3,[4,[5,[6,[7,[8,[9,[10,[11,[12,[13,[14,]]]]]]]]]]]]]] res = 0 def sum_list(test_list, res): f

递推算法与二分算法

递推算法: (一)斐波那契数列 以下数列0 1 1 2 3 5 8 13 21 …被称为斐波纳契数列. 这个数列从第3项开始,每一项都等于前两项之和. 输入一个整数N,请你输出这个序列的前N项. 输入格式 一个整数N. 输出格式 在一行中输出斐波那契数列的前N项,数字之间用空格隔开. 数据范围 0<N<460<N<46 输入样例: 5 输出样例: 0 1 1 2 3 #include<iostream> using namespace std; int f[100];