算法学习笔记(三)问题的转化与高精度运算

问题:设购票点没有任何的零钱,票价50美元,现有m人手持50美元,n人手持100美元,求这样m+n个人构成的队伍有多少种排队方法可以使得整个售票过程不中断。

分析:对于这个问题,经过简单的模拟可以发现,每个手持100的前面必须有一个手持50的,同样如果有k个手持100的连续出现,则前面至少连续k次50。

这样一来,可以设手持50元的为+1,手持100元的为-1,设ai为为第i个人所对应的值,则问题转化为数组的部分和a1+a2+...+ak≥0,其中k≤m+n,为了求这样的数列的个数,需要使用组合数学的相关知识,这个问题可以使用现成的结论,第n个Catalan数,公式为:

在运算时,要特别注意m<n的情况,在这种情况下,无法满足要求。

算法实现:由于购票人数一般很多,这就带来了高精度运算的问题,不再能使用传统的运算方式。高精度运算的一种方法是把数据都转化为字符串,再对字符串定义运算。

①将数据转化为字符串

将数据转化为字符串的算法是十分有效的,尤其在单片机的编程当中,一般的算法是将整数从高位到低位顺次存入一个字符数组,这时字符数组所存的数据是反的,这时候需要再设定一个数组将其反过来,通过查阅资料,发现了一种比较号的算法,它的亮点是①充分利用i++的先运算后++特性简化代码,②省略对位数的判断,而采用while(n)判断所有位数是不是都已经取完,③在字符数组尾部赋0从而保证字符串在最后一个有效位后结束。代码如下:

void NumToString(int n, char* s)
{
    int i,j,temp[8]; //临时数组,先把数位存入其中,倒序
    if(n == 0)
    {
        s[0] = ‘0‘;
        s[1] = 0; //在有效位之后添加\0
        return;
    }
    i = 0;
    while(n) //判断有没有取完所有位
    {
        temp[i++] = n % 10; //先存入,后i++
        n /= 10;
    }
    i -= 1;
    j = 0;
    while(i >= 0)
        s[j++] = temp[i--] + ‘0‘; //将倒序的数组正序存入s数组,s数组存有最终结果
    s[j] = 0;
}

②重新定义字符串的运算,以乘法为例,算法如下:

void mul(char* m, char* n, char* res)
{
    int i,j,len1,len2;
    len1 = strlen(m);
    len2 = strlen(n);
    int *r = new int[len1 + len2 + 1]; //乘积的长度,例如两位乘以两位,最多为5位,因此多加1
    for(i = 0; i <= len1 + len2; i++) r[i] = 0; //初始化乘积存储数组
    for(i = 0; i < len1; i++)
        for(j = 0; j < len2; j++)
            r[i + j + 1] += (m[i] - ‘0‘)*(n[j] - ‘0‘); //依次从最高位、次高位直至最低位进行运算,注意,这里的最高位实际为次高位,因为
                                                       //真正的最高位只能通过进位获得,而不是通过乘运算
    for(i = len1 + len2 - 1; i >=1; i--) //从最低位开始处理进位
    {
        if(r[i] > 9) 
        {
            int temp = r[i] / 10; //储存进位数
            r[i] %= 10; //将进位后的数组存在这一位
            r[i-1] += temp; //进位运算,同时处理r[0]这一位(进位可能到达的最高位,没有则为0)
        }
    }
    for(i = 0; i<len1+len2;i++) cout << r[i] << " ";
    cout << endl;
    //经过这样的运算,将会得到数据字符串,其中最高位在r[0]内,最低位在r[len1+len2-1]内
    for(i = 0; i < len1+len2; i++) //判断是否乘积为0,为0则i会自加到len1+len2
        if(r[i] != 0) break;
    if(i == len1 + len2)
    {
        res[0] = ‘0‘;
        res[1] = 0;
        return;
    }
    //如果乘积不为0,会进行下面的运算,顺次将r[0]到r[len1+len2-1]存入res[0]到res[len1+len2-1]
    j = 0;
    while(i < len1 + len2)
    {
        res[j++] = r[i++] + ‘0‘;
    }
    res[j] = 0;
    delete[] r;
}
时间: 2024-10-21 21:03:30

算法学习笔记(三)问题的转化与高精度运算的相关文章

算法学习笔记三(插点法)

插点法,也就是Flord算法,主要应用于 求出有权图中最短路径. 算法核心: for(k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++) { if(d[i][k]+d[k][j]<d[i][j]) d[i][j]=d[i][k]+d[k][j]; }

EM算法学习笔记2:深入理解

文章<EM算法学习笔记1:简介>中介绍了EM算法的主要思路和流程,我们知道EM算法通过迭代的方法,最后得到最大似然问题的一个局部最优解.本文介绍标准EM算法背后的原理. 我们有样本集X,隐变量Z,模型参数θ,注意他们3个都是向量,要求解的log似然函数是lnp(X|θ),而这个log似然函数难以求解,我们假设隐变量Z已知,发现lnp(X,Z|θ) 的最大似然容易求解. 有一天,人们发现引入任意一个关于隐变量的分布q(Z),对于这个log似然函数,存在这样一个分解: lnp(X|θ)=L(q,θ

CCNA学习笔记三——STP生成树协议

广播风暴:当网络中存在物理环路,会产生广播风暴 STP协议:Spanning Tree Protocol(生成树协议) 逻辑上断开环路,防止广播风暴的产生 STP算法:(所有选择都是比小-小的当选) 选择根网桥(Root Bridge):在网络中的所有交换机中选择一台 选择依据:网桥ID(网桥优先级+MAC地址) 选择根端口(Root Ports):在所有非根网桥中选择一个 选择依据:(1)根路径成本最低 (2)直连网桥ID最小 (3)端口ID最小 选择指定端口(Designated Ports

Ajax学习笔记(三)

三.jQuery库详解 1.使用jQuery之后,javascript操作的不再是HTML元素对应的DOM对象,而是包装DOM对象的jQuery对象.js通过调用jQuery对象的方法来改变它所包装的DOM对象的属性,从而实现动态更新HTML页面. 由此可见,使用jQuery动态更新HTML页面只需以下两个步骤: (1)获取jQuery对象.jQuery对象通常是DOM对象的包装 (2)调用jQuery对象的方法来改变自身.当jQuery对象被改变时,jQuery包装的DOM对象随之改变,HTM

马哥学习笔记三十——tomcat

Java体系结构包含四个独立却又彼此相关的技术: Java程序设计语言 Java API Java Class文件格式 JVM: Java Virtual Machine JVM的实现方式: 1.一次性解释器,解释字节码并执行: 2.即时编译器(just-in-time complier) 依赖于更多内存缓存解释后的结果 3.自适应编译器 缓存20%左右代码,提高80%左右的速度: 运行时数据区: 线程私有内存区: 程序计数器 java虚拟机栈 线程共享内存区: 方法区 堆:java自动内存回收

由LCS到编辑距离—动态规划入门—算法学习笔记

一切计算机问题,解决方法可以归结为两类:分治和封装.分治是减层,封装是加层. 动态规划问题同样可以用这种思路,分治. 它可以划分为多个子问题解决,那这样是不是用简单的递归就完成了?也许是的,但是这样会涉及太多的不便的操作.因为子问题有重叠! 针对这种子问题有重叠的情况的解决,就是提高效率的关键. 所以动态规划问题可以总结为:最优子结构和重叠子问题. 解决这个子问题的方式的关键就是:memoization,备忘录. 动态规划算法分以下4个步骤: 描述最优解的结构 递归定义最优解的值 按自底向上的方

Ninject学习笔记&lt;三&gt;

ASP.NET MVC学前篇之Ninject的初步了解 1.介绍 废话几句,Ninject是一种轻量级的.基础.NET的一个开源IoC框架,在对于MVC框架的学习中会用到IoC框架的,因为这种IoC开源框架有很多,本篇的主题只有一个,就是让阅读过本篇幅的朋友逗知道IoC框架在项目中的作用,以及它的重要性. 这样做的目的是以便在以后的学习工作中选择自己中意的一个IoC框架来学习.使用,或者是自己去实现一个.好了,不废话了. 2.环境准备 1.新建个4.0Framework的一个控制台应用程序项目,

小猪的数据结构学习笔记(三)

小猪的数据结构学习笔记(三) 线性表之单链表 本章引言: 上一节中我们见识了第一个数据结构--线性表中的顺序表; 当你把操作的代码自己写几遍就会有点感觉了,如果现在让你写顺序表的 插入算法,你能够想出大概的代码么?如果可以,那么你就可以进入新的章节了; 否则,还是回头看看吧!在本节,我们将迎来线性表的链式表示--单链表 单链表和顺序表有什么优势和劣势呢?单链表的头插法和尾插法有什么不同呢? 请大家跟随笔者的脚步来解析线性表中的单链表把! 本节学习路线图 路线图解析: ①先要理解顺序表和单链表各自

Caliburn.Micro学习笔记(三)----事件聚合IEventAggregator和 Ihandle&lt;T&gt;

Caliburn.Micro学习笔记(三)----事件聚合IEventAggregator和 Ihandle<T> 今天 说一下Caliburn.Micro的IEventAggregator和IHandle<T>分成两篇去讲这一篇写一个简单的例子 看一它的的实现和源码 下一篇用它们做一个多语言的demo 这两个是事件的订阅和广播,很强大,但用的时候要小心发生不必要的冲突. 先看一下它的实现思想 在Caliburn.Micro里EventAggregator要以单例的形式出现这样可以

OpenCV for Python 学习笔记 三

给源图像增加边界 cv2.copyMakeBorder(src,top, bottom, left, right ,borderType,value) src:源图像 top,bottem,left,right: 分别表示四个方向上边界的长度 borderType: 边界的类型 有以下几种: BORDER_REFLICATE # 直接用边界的颜色填充, aaaaaa | abcdefg | gggg BORDER_REFLECT # 倒映,abcdefg | gfedcbamn | nmabcd