算法很美——数学问题

算法很美——数学问题

  • 题1:天平称重
    问题描述:
    用天平称重时,我们希望用尽可能少的砝码组合称出尽可能多的重量。
    砝码重量分别是1,3,9,27,81……3的指数次幂,每种重量砝码只有一个
    则它们可以组合称出任意整数重量(砝码允许放在左右两个盘中)。
    本题目要求编程实现:对用户给定的重量,给出砝码组合方案。
    例如:
    用户输入:
    5
    程序输出:
    9-3-1
    用户输入:
    19
    程序输出:
    27-9+1
    要求程序输出的组合总是大数在前小数在后。
    可以假设用户的输入的数字符合范围1~1000000
    思路:3的指数次幂——可以想到用3进制,将输入转为三进制,按位输出,而二进制中可能出现2,但同一重量砝码只有一个,考虑加一减一,如下
    19的三进制表示:
    2 0 1:二位加一变成:1 0 0 1
    减一变成:1 -1 0 1
    这样可以使三进制表示中只有1,0,-1
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int k[10],num;
void fun(int a)
{
    num=0;int flag=0;
    while(a)
    {
        if(a%3==2)
        {k[num++]=-1;a/=3;a++;}
        else {k[num++]=a%3;a/=3;}
    }//printf("num=%d\n",num);
    //for(int i=num-1;i>=0;i--)printf("%d ",k[i]);printf("\n");
    for(int i=num-1;i>=0;i--)
    {
        //printf("i+1=%d pow=%d\n",i+1,pow(3,i+1));
        if(k[i]<0)printf("-%d",(int)pow(3,i));
        else if(k[i]==1)
        {
            if(flag)
            printf("+%d",(int)pow(3,i));
            else printf("%d",(int)pow(3,i));
        }
        flag=1;
    }printf("\n");
}
int main()
{
    int n;
    while(scanf("%d",&n)!=-1)
        fun(n);
    return 0;
}

  • 题2:Nim游戏
    在这里插入图片描述
    Nim游戏特点
    n个部分,任意取,至少一个,轮流,取光为赢,两人均为最优策略
    思路
    所有堆进行异或
    如果结果为0,则任何取法都将使之变成非零
    如果结果不是零,则总有取法使之变成零
    所以结果为零,先手输;结果非零,先手赢
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
void fun(int a[],int n)
{
    int t=a[0];
    for(int i=1;i<n;i++)t^=a[i];
    if(t)
    {
        printf("WIN:1\n");return;
    }
    else
    {
        printf("WIN:2\n");return;
    }
}
int main()
{
    int n;
    while(scanf("%d",&n)!=-1)
    {
        int a[n];
        for(int i=0;i<n;i++)scanf("%d",&a[i]);
        fun(a,n);
    }
    return 0;
}

  • 题3:Georgia ans Bob——Nim的变形

题目描述
Georgia和Bob在玩游戏。一个无限长的棋盘上有N个旗子,第i个棋子的位置可以用Pi表示。现在Georgia先走。每个人每一次可以把一枚棋子向左移动任意个格子,但是不能超越其他棋子,也不能和其他棋子处在同一个格子里。如果轮到某一个人的时候Ta再也不能移动棋子了,就判负。现在每个测试数据给定一种情况,如果Georgia会赢,输出“Georgia will win”,如果Bob会赢,输出“Bob will win”
思路:
“直到不能移动——间隔为0”,“可以向左移动任意格,不能跨越至少移动1格”
可以考虑用Nim问题思路解决

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
void Nim(int a[],int n)
{
    int res=a[0];
    for(int i=1;i<n;i++)
        res^=a[i];
    if(res)
    {
        printf("WIN:1\n");return;
    }
    else printf("WIN:2");
}
void fun(int a[],int n)
{
    int b[n/2+1],j=0;
    if(n%2==0)
    {
        for(int i=1;i<n;i+=2)
            b[j++]=a[i]-a[i-1]-1;
    }
    else
    {
        for(int i=0;i<n;i+=2)
            b[j++]=i==0?a[0]:a[i]-a[i-1]-1;
    }
    Nim(b,j);
}
int main()
{
    int n;
    while(scanf("%d",&n)!=-1)
    {
        int a[n];
        for(int i=0;i<n;i++)scanf("%d",&a[i]);
        sort(a,n);
        fun(a,n);
    }
    return 0;
}

  • 重要求和公式
    等差:
    平方和、立方和:
    在这里插入图片描述
    等比:
    在这里插入图片描述
    调和级数:=lnn
    在这里插入图片描述
  • gcd
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int gcd(int m,int n)
{
    return n==0?m:gcd(n,m%n);
}
int main()
{
    int a,b;
    while(scanf("%d %d",&a,&b)!=-1)printf("%d\n",gcd(a,b));
    return 0;
}

  • 题3:
    在两点间线段上,有几个点横纵坐标均为整数
    gcd(d1,d2)-1个
  • 裴蜀公式(贝祖公式)
    在这里插入图片描述
  • 素数筛——求第N个素数
    求第N个素数的普通算法(看从2到根号N是否有数能将N除尽)——O(N根号N)
    为提高效率可使用埃氏筛,依次筛掉2,3,4,5······的倍数,得到一个素数表——O(nlognlogn)
    思路:先开空间(数组),再筛倍数
    开空间大小?
    有素数定理可得,在X范围内,大致有X/logX个素数
    例题:求第100002个素数
#include <iostream>
#include <bits/stdc++.h>
#define N 100002
using namespace std;
void fun()
{
    long n=2,i=2;
    while(n/log(n)<N)
        n++;
    n+=5;
    printf("n=%ld\n",n);
    int arr[n];
    memset(arr,0,sizeof(arr));
    while(i<n)
    {
        if(arr[i]!=0)
        {
            i++;
            continue;
        }
        int k=2;
        while(i*k<n)
        {arr[i*k]=-1;k++;}
        i++;
    }
    long num=0;
    for(int j=2;j<n;j++)
    {
        if(arr[j]==0)num++;
        if(num==N)
        {
            printf("%d\n",j);
            return;
        }
    }
    printf("num=%ld\n",num);
}
int main()
{
    fun();
    return 0;
}

100002超时

  • 快速幂——求n的m次幂
    求n的平方的平方的平方···直到大于所求,剩下的幂次递归
#include <iostream>
#include <bits/stdc++.h>
#define N 100002
using namespace std;
int fun(int n,int m)
{
    if(m==1)return n;
    if(m==0)return 1;
    int ans=n,i=1;
    while(i*2<=m)
    {ans*=ans;i*=2;}
    return ans*fun(n,m-i);
}
int main()
{
    int n,m;
    while(scanf("%d %d",&n,&m)!=-1)
        printf("%d\n",fun(n,m));
    return 0;
}
  • 快速幂方法二:巧算
    例:a的10次方
    10:(1010)2
    所以,a的10次方等于a的2的3次方*a的2的1次方
#include <iostream>
#include <bits/stdc++.h>
int fun(int m,int n)
{
    int result=1,pingfangshu=m;
    if(n==0)return 1;
    while(n)
    {
        if((n&1)==1)
        result*=pingfangshu;
        pingfangshu*=pingfangshu;
        n>>=1;
    }
    return result;
}
int main()
{
    int m,n;
    while(scanf("%d %d",&m,&n)!=-1)
    printf("%d\n",fun(m,n));
    return 0;
}

  • 斐波那契数列的另一种求法——类似于幂运算
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值