算法很美 笔记 6.数学问题

6.数学问题

题1:天平称重:变种3进制

用天平称重时,我们希望用尽可能的砝码组合称出尽可能的重量。

如果有无限个砝码,重量分别是1,3,9,27,81,……等3的指数幂
神奇之处在于用它们可以称出任意整数重量(砝码允许放在左右两个盘中)。

本题目要求编程实现:对用户给定的重量,给出砝码组合方案。
用户输入:5
程序输出:9-3-1
用户输入:19
程序输出:27-9+1
要求程序输出的组合总是大数在前小数在后。
可以假设用户的输入的数字符合范围1~121。

5的二进制对应101,4+1等于5,二进制对应两种状态,有和没有
$$
12^2+02^1+1*2^0
$$
三进制0代表没有,1代表有,2代表前一位+1,此位变-1,代表减

2的这一位+1,变3,往进1,多加1,此为-1

import java.util.Arrays;
import java.util.Scanner;

public class 天平称重 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        String s=Integer.toString(sc.nextInt(),3);
        int[] arr=new int[s.length()+1];
        for (int i = 0; i < s.length(); i++) {
            arr[i+1]=s.charAt(i)-48;
        }
        for (int i = arr.length-1; i>0 ; i--) {
                if(arr[i]==2){
                    arr[i]=-1;
                    arr[i-1]++;
                }
                if(arr[i]==3){//22的这种情况1 0 -1
                    arr[i]=0;
                    arr[i-1]=1;
                }
        }
        boolean isFirst=true;
        for (int i = 0; i < arr.length; i++) {
            if(arr[i]==1){
                if(isFirst){
                    System.out.print((int)Math.pow(3,arr.length-i-1));
                    isFirst=false;
                }else{
                    System.out.print("+"+(int)Math.pow(3,arr.length-i-1));
                }
            }
            if(arr[i]==-1){
                System.out.print("-"+(int)Math.pow(3,arr.length-i-1));
            }
        }
    }
}

题2:Nim游戏

一共有N堆石子,编号1..n ,第i堆中有个a[门]个石子。每一次操作Alice和Bob可以从任意-堆石子中取出任意数量的石子至少取一颗,至多取出这一-堆剩下的所有石子。两个人轮流行动,取光所有石子的- -方获胜。Alice为先手。
给定a,假设两人都采用最优策略,谁会获胜?

个数^ 先手,

非0,赢,对手一动,变0,

0,输,对手变成非0

/**
 * 几个数字做模2的加法,如果为0,无论怎么拿结果都不为0 ,如果不为0 ,总有办法改变一个数字把它变成0
 *  11
 * 100
 * 101
 * ----
 * 010
 * 现在不为0,我们改变第二位就可以将结果变为0
 */
public class Case02_Nim {
  public static void main(String[] args) {
    int[] A = {5, 10, 15};
    boolean res = solve(A);
    System.out.println(res);
  }
  static boolean solve(int[] A) {
    int res = 0;
    for (int i = 0; i < A.length; i++) {
      res ^= A[i];
      // System.out.println(Integer.toBinaryString(A[i]));
    }
    return res != 0;//不为0就会赢
  }
}

题3:Georgia and Bob

Georgia and Bob decide to play a self-invented game. They draw a row of grids on paper, number the grids from left to right by 1, 2, 3, ..., and place N chessmen on different grids, as shown in the following figure for example:
在这里插入图片描述
Georgia and Bob move the chessmen in turn. Every time a player will choose a chessman, and move it to the left without going over any other chessmen or across the left edge. The player can freely choose number of steps the chessman moves, with the constraint that the chessman must be moved at least ONE step and one grid can at most contains ONE single chessman. The player who cannot make a move loses the game.

Georgia always plays first since "Lady first". Suppose that Georgia and Bob both do their best in the game, i.e., if one of them knows a way to win the game, he or she will be able to carry it out.

Given the initial positions of the n chessmen, can you predict who will finally win the game?
Input
The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. Then T cases follow. Each test case contains two lines. The first line consists of one integer N (1 <= N <= 1000), indicating the number of chessmen. The second line contains N different integers P1, P2 ... Pn (1 <= Pi <= 10000), which are the initial positions of the n chessmen.
Output
For each test case, prints a single line, "Georgia will win", if Georgia will win the game; "Bob will win", if Bob will win the game; otherwise 'Not sure'.
Sample Input
2
3
1 2 3
8
1 5 6 7 9 12 14 17
Sample Output
Bob will win
Georgia will win

大致题意是说给一个很长的棋盘,一些地方有棋子,每个格子只能放1个棋子。每次必须要向左移动1个棋子,但不能移除棋盘,也不能超过它左边的第一个棋子。求先手是否必胜。

对于某两个棋子,如果他们两个靠在了一起,那么它们对手的状态就是一个必败状态。

这一点很显然,如果两个棋子贴在一起,先手只能移动前面的棋子,而后手可以通过紧跟先手来继续使先手拿到必败状态。

我们把2个棋子看做1组,之间的空位数看做一堆石子,最后按照nim游戏计算即可

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    private static String deal(int[] A) {
        int len = A.length;
        Arrays.sort(A);//7 3 2 9  最坑之处
        int res = 0;
        if ((len & 1) == 1) {//奇数
            for (int i = 0; i < len; i += 2) {
                res ^= (i == 0) ? (A[0] - 1) : (A[i] - A[i - 1] - 1);
            }
        } else {
            for (int i = 1; i < len; i += 2) {
                res ^= (A[i] - A[i - 1] - 1);
            }
        }
        if (res == 0) {
            return "Bob will win";
        } else {
            return "Georgia will win";
        }

    }
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int num=sc.nextInt();
        for (int i = 0; i <num ; i++) {
            int n=sc.nextInt();
            int[] arr=new int[n];
            for (int j = 0; j < n; j++) {
                arr[j]=sc.nextInt();
            }
            System.out.println(deal(arr));
        }
    }
}

题目链接

求和公式

等差级数

$$
\sum_{i=1}^{n} k=1+2+\cdots+n
$$

是一个等差级数,它的值为
$$
\begin{aligned}
\sum_{i=1}^{n} k &=\frac{1}{2} n(n+1) \
&=\Theta\left(n^{2}\right)
\end{aligned}
$$
平方和与立方和的求和公式如下;
$$
\begin{aligned}
&\sum_{k=0}^{n} k^{2}=\frac{n(n+1)(2 n+1)}{6}\
&\sum_{k=0}^{n} k^{3}=\frac{n^{2}(n+1)^{2}}{4}
\end{aligned}
$$

几何级数

对于实数x≠1,和式
$$
\sum_{k=0}^{n} x^{k}=1+x+x^{2}+\cdots+x^{n}
$$
是一个几何级数(或称指数级数),它的值是
$$
\sum_{k=0}^{n} x^{k}=\frac{x^{n+1}-1}{x-1}
$$
当和是无穷的且|x|<1时,即得到无穷递减几何级数
$$
\sum_{k=0}^{\infty} x^{k}=\frac{1}{1-x}
$$

调和级数

对正整数n,第n个调和数是
$$
H_{n}=1+\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+\dots+\frac{1}{n}=\sum_{k=1}^{n} \frac{1}{k}=\ln n+O(1)
$$

欧几里得算法

求最大公因数

两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数

a>b gcd(a,b)=gcd(b,a%b)

public static  int gcd(int m,int n){
    return n==0?m:gcd(n,m%n);
}

如果m<n,执行一次后变为,gcd(n,m) 小的数模大的数等于小的数,这样就大的数在前,小的数在后了

裴蜀(贝祖)等式

  • 对任何整数a、b和它们的最大公约数d ,关于未知数x和y的线性丢番图方程(称为裴蜀等式) :ax + by=m有整数解时当且仅当m是d的倍数裴蜀等式有解时必然有无穷多个整数解,每组解x、y都称为裴蜀数,可用扩展欧几里得算法(Extended Euclidean algorithm)求得。

  • 方程12x + 42y=6有解 gcd(12,42)=6 为6的倍数

  • 特别地,方程ax+ by=1有整数解当且仅当整数a和b互素

  • 扩展欧几里得算法就是在求a、b的最大公约数m=gcd(a,b)的同时,求出贝祖等式ax + by = m的一-个解(x~0~,y~0~)

  • 如何递推?
    • x=y~1~
    • y=x~1~-a/b*y~1~
  • 通解:
    • x=x~0~+ (b/gcd)*t 所有的x对b同模
    • y=y~0~-(a/gcd)*t 所有的y对a同模
  • 如果想要得到x大于0的第一个解?由x的通解,所有解模(b/gcd)同余,b/=gcd,得到的解%b后,绝对值<b

    ,为了防止为负数,+b,但是此时很可能原来就是正数,多加了一个b,这里统一%b

  • b/=d; x=(x~0~%b+b)%b

gcd(a,b)=gcd(b,a%b)
ax+by=bx~1~+(a%b)y~1~
ax+by=bx~1~+[a-(a/b)*b]y~1~
ax+by=ay~1~+b[x~1~-(a/b)*y~1~]
由此推得
$$
\begin{aligned}
&x=y_{1}\
&y=x_{1}-(a / b) \cdot y_{1}
\end{aligned}
$$
其中x~1~何y~1~为下层,x和y为上层,由于最后一层是是x=1,y=0,所有可以由下层往上层推

  • 求2x+7y=1 的特解

在这里插入图片描述

题4:一步之遥

从昏迷中醒来,小明发现自己被关在X星球的废矿车里。矿车停在平直的废弃的轨道上。他的面前是两个按钮,分别写着“F”和“B”。小明突然记起来,这两个按钮可以控制矿车在轨道上前进和后退。按F,会前进97米。按B会后退127米。透过昏暗的灯光,小明看到自己前方1米远正好有个监控探头。他必须设法使得矿车正好停在摄像头的下方,才有机会争取同伴的援助。或许,通过多次操作F和B可以办到。矿车上的动力已经不太足,黄色的警示灯在默默闪烁...每次进行 F 或 B 操作都会消耗一定的能量。小明飞快地计算,至少要多少次操作,才能把矿车准确地停在前方1米远的地方。
请填写为了达成目标,最少需要操作的次数。
97x-127y=1
ax+by=m

或者暴力,x从1遍历,y从1遍历,看结果是否为1

/**
     * 扩展欧几里得
     * 调用完成后xy是ax+by=gcd(a,b)的解*/
static long x,y;
public static long ext_gcd(long a,long b){
    if(b==0){
        x=1;
        y=0;
        return a;
    }
    long res=ext_gcd(b,a%b);
    long temp=x;//先备份x
    x=y;//更新x
    y=temp-a/b*y;//更新y
    return res;

}
/**
     * 线性方程
     * ax+by=m 当m时gcd(a,b)倍数时有解
     * 等价于ax = m mod b*/
public static long linearEquation(long a,long b,long m) throws Exception {
    long d=ext_gcd(a,b);
    if(m%d!=0){//m不是gcd(a,b)的倍数,这个方程无解
        throw new Exception("无解");
    }
    long n=m/d;//约一下,考虑m是d的倍数
    x*=n;
    y*=n;
    return d;
}
public static void main(String[] args) {
    try {
        System.out.println(linearEquation(97,-127,1));
        System.out.println(x+" "+y);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
55+42=97

  • 计算除以m的余数,叫做对m取模

  • 将a,b对m取模的结果相同,记为a≡b(mod m),即
    a mod m==b mod m

  • 如果a=b(mod m),且c≡d(mod m):
    • a+b≡c+d (mod m)
    • ab≡cd (mod m)

求解模线性方程(一次同余方程)

  • linear congruence theorem

  • 扩展欧几里德算法求解模线性方程的方法:

  • 同余方程ax≡b (mod n)对于未知数x有解,当且仅当b是gcd(a,n)的倍数。且方程有解时,方程有gcd(a,n)个解。

  • 求解方程ax≡b (mod n)相当于求解方程ax+ny= b, (x, y为整数)

题5:青蛙的约会

两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。

Input

输入只包括一行5个整数x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。

Output

输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行"Impossible"

Sample Input

1 2 3 4 5

Sample Output

4

mt + x ≡ nt+ y (mod L)转化为(m - n)t ≡ y - x(mod L)参数 a=m-n b=L m=y-x

import java.util.Scanner;

public class 青蛙的约会 {
    static long x,y;
    public static long gcd(long a,long b){
        if(b==0){
            x=1;
            y=0;
            return a;
        }
        long res=gcd(b,a%b);
        long t=x;
        x=y;
        y=t-a/b*y;//注意这里是t,是旧的x
        return res;
    }
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        long x1=sc.nextLong();//注意不能起名为x
        long y1=sc.nextLong();
        long m=sc.nextLong();
        long n=sc.nextLong();
        long L=sc.nextLong();
        long d=gcd(m-n,L);//d位最大公约数gcd ax+by=m 中的m为y1-x1
        if((y1-x1)%d!=0){//是否是公约数的整数倍
            System.out.println("Impossible");
        }else {
            x*=(y1-x1)/d;//(y1-x1)/d得倍数,再使x翻倍
            long xi=Math.abs(L/d);// ax+by=m 中xi为b/gcd
            System.out.println((x%xi+xi)%xi);
        }
    }
}

题目链接

模的逆元

  • 同余方程ax≡1 (mod n ),gcd(a,n)= 1时有解。a和n互为素数

  • 这时称求出的X为a的对模n的乘法逆元。

  • 对于同余方程ax≡ 1(modn),gcd(a,n)= 1的求解就是求解方程ax+ ny= 1, x, y为整数。这个可用扩展欧几里德算法求出,原同余方程的唯一-解就是用扩 展欧几里德算法得出的x

题6:A/B

要求(A/B)%9973,但由于A很大,我们只给出n(n=A%9973)(我们给定的A必能被B整除,且gcd(B,9973) = 1)。

Input

数据的第一行是一个T,表示有T组数据。
每组数据有两个数n(0 <= n < 9973)和B(1 <= B <= 10^9)。

Output

对应每组数据输出(A/B)%9973。

Sample Input

2
1000 53
87 123456789

Sample Output

7922
6060

(A/B)%9973等价于(AB^-1^)%9973等价于A%9973B^-1^

所以结果为n乘以B的逆元

import java.util.Scanner;

public class AB {
    static long x,y;
    public static long gcd(long a,long b){
        if(b==0){
            x=1;
            y=0;
            return a;
        }
        long res=gcd(b,a%b);
        long t=x;
        x=y;
        y=t-a/b*y;
        return res;
    }
    public static void main(String[] args) {
        final int mod=9973;
        Scanner sc=new Scanner(System.in);
        int N=sc.nextInt();
        for (int i = 0; i < N; i++) {
            long n=sc.nextLong();
            long b=sc.nextLong();
            gcd(b,mod);//Bx≡1(mod 9973)  Bx+9973y=1
            //由于gcd(B,9973) = 1)结果为1,是1的整数倍,x不用翻倍
            System.out.println((((n*x)%mod)+mod)%mod);//得到的n*x,变成最小正数
        }
    }
}

题目链接

同余方程组

  • 孙子定理?有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?

$$
(S):\left{\begin{array}{ll}
x=a_{1} & \left(\bmod m_{1}\right) \
x \equiv a_{2} & \left(\bmod m_{2}\right) \
\vdots & \
x \equiv a_{n} & \left(\bmod m_{n}\right)
\end{array}\right.
$$

  • 中国剩余定理?

  • 大衍求一术?

  • 一元线性同余方程组

/**
 * x = a1(%m1)
 *   = a2(%m2)
 *   = a3(%m3)
 *  x = a1+m1k1 (1)
 *  x = a2+m2k2
 *  ==> m1k1 - m2k2 = a2 - a1这是一个线性方程,可解出k1 <--- linearEquation(m1,-m2,a2-a1)
 *  带回(1),得特解x0 = a1+m1*k1 --> 解系 x =x0 + k*lcm(m1,m2) 得一个新方程 x = x0 (mod lcm(m1,m2))
 *  */
public static long linearEquationGroup(long[] r, long[] m) throws Exception {
  int len = r.length;
  if (len == 0 && r[0] == 0) return m[0];
  long R = r[0];
  long M = m[0];
  for (int i = 1; i < len; i++) {
    //这里往前看是两个方程
    long c = r[i] - R;
    long d = linearEquation(M, m[i], c);
    //现在的static x是k1,用k1求得一个特解
    long x0 = R + M * x;//特解-》解系:X=x0+k*lcm(m1,m2)->得新方程: X 三 x0 mod lcm
    long lcm = M * m[i] / d;//这是新的m
    M = lcm;
    R = x0 % lcm;//x0变成正数
  }
  //合并完之后,只有一个方程 : X mod M = R
  while (R < 0)
    R += M;
  return R;
}

题7:生理周期

人生来就有三个生理周期,分别为体力、感情和智力周期,它们的周期长度为23天、28天和33天。每一个周期中有一天是高峰。在高峰这天,人会在相应的方面表现出色。例如,智力周期的高峰,人会思维敏捷,精力容易高度集中。因为三个周期的周长不同,所以通常三个周期的高峰不会落在同一天。对于每个人,我们想知道何时三个高峰落在同一天。对于每个周期,我们会给出从当前年份的第一天开始,到出现高峰的天数(不一定是第一次高峰出现的时间)。你的任务是给定一个从当年第一天开始数的天数,输出从给定时间开始(不包括给定时间)下一次三个高峰落在同一天的时间(距给定时间的天数)。例如:给定时间为10,下次出现三个高峰同天的时间是12,则输出2(注意这里不是3)。

Input

输入四个整数:p, e, i和d。 p, e, i分别表示体力、情感和智力高峰出现的时间(时间从当年的第一天开始计算)。d 是给定的时间,可能小于p, e, 或 i。 所有给定时间是非负的并且小于365, 所求的时间小于21252。

当p = e = i = d = -1时,输入数据结束。

Output

从给定时间起,下一次三个高峰同天的时间(距离给定时间的天数)。

采用以下格式:
Case 1: the next triple peak occurs in 1234 days.

注意:即使结果是1天,也使用复数形式“days”。

Sample Input

0 0 0 0
0 0 0 100
5 20 34 325
4 5 6 7
283 102 23 320
203 301 203 40
-1 -1 -1 -1

Sample Output

Case 1: the next triple peak occurs in 21252 days.
Case 2: the next triple peak occurs in 21152 days.
Case 3: the next triple peak occurs in 19575 days.
Case 4: the next triple peak occurs in 16994 days.
Case 5: the next triple peak occurs in 8910 days.
Case 6: the next triple peak occurs in 10789 days.
import java.util.Scanner;
public class 生理周期 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int p,e,i,d;
        int time=1;
        while(true) {
            p=sc.nextInt();
            e=sc.nextInt();
            i=sc.nextInt();
            d=sc.nextInt();
            if(p==-1 && e==-1 && i==-1 && d==-1)
                break;
            int lcm=21252;  // lcm(23,28,33)
            int n=(5544*p+14421*e+1288*i-d+21252)%21252;
            if(n==0)
                n=21252;
            System.out.println("Case "+(time++)+": the next triple peak occurs in "+n+" days.");
        }
    }
}

题目链接

  • n是不是素数
    • 2~n-1间是否有整数能整除n?
    • 如果d是n的约数,那么n/d也是n的约数,由n = d * (n/d) 可知,d<=根号n,所以检查2~根号n之间是否有整数能整除n

质因子分解

public static Map<Integer,Integer> primeFactor(long n) {
    Map<Integer,Integer> mp=new HashMap<Integer, Integer>();
    for (int i = 2; i<=n ; i++) {
        while(n%i==0){
            Integer v= mp.get(i);
            if(v==null){
                mp.put(i,1);
            }else {
                mp.put(i,v+1);
            }
            n/=i;
        }
    }
    return mp;
}

例题1

对N!进行质因子分解。
输入格式
输入数据仅有一行包含一个正整数N,N<=10000。
输出格式
输出数据包含若干行,每行两个正整数p,a,中间用一个空格隔开。表示N!包含a个质因子p,要求按p的值从小到大输出。
输入
10
输出
2 8
3 4
5 2
7 1
说明/提示
10!=3628800=(2^8)(3^4)(5^2)*7

import java.util.Scanner;
public class 质因子分解 {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int[] a=new int[n+1];
        for (int i = 2; i <=n ; i++) {
            int t=i;
            for (int j = 2; j <=i ; j++) {
                while(t%j==0){
                    a[j]++;
                    t/=j;
                }
            }
        }
        for (int i = 0; i < a.length; i++) {
            if(a[i]!=0){
                System.out.println(i+" "+a[i]);
            }
        }
    }
}

题目链接

例题2

读入一个自然数,将nn分解为质因子连乘的形式输出。

Input

有多组测试数据。输入的第一行是整数T(0<T<=10000),表示测试数据的组数。每一组测试数据只有一行,包含待分解的自然数nn。该行没有其它多余的符号。1<n<2^31^。

Output

对应每组输入,输出一行分解结果,具体样式参看样例。该行不能有其它多余的符号。

Sample Input

3
756
2
2093333998

Sample Output

756=2*2*3*3*3*7
2=2
2093333998=2*7*7*17*43*29221
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;

public class 质因子分解2 {
    public static Map<Integer,Integer> primeFactor(int n){
        Map<Integer,Integer> map=new TreeMap<Integer, Integer>();
        for (int i = 2; i*i<=n; i++) {//i<根号n不超时且不需要提前判断是否为素数
            //i<n会超时
            Integer num=0;
            while(n%i==0){
                num++;
                n/=i;
            }
            if(num!=0){
                map.put(i,num);
            }
        }
        if(n!=1){//如果变成i<根号n,会漏掉最后一个这里补上
            map.put(n,1);
        }
        return map;
    }
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int N=sc.nextInt();
        for (int i=0;i<N;i++) {
            int n=sc.nextInt();
            Map<Integer,Integer> map=primeFactor(n);
            StringBuilder  sb=new StringBuilder();
            for (Map.Entry<Integer,Integer> entry:map.entrySet()){
                for (int j = 0; j <entry.getValue() ; j++) {
                    sb.append("*"+entry.getKey());
                }
            }
            System.out.println(n+"="+sb.substring(1));
        }
    }
}
import java.util.Scanner;

public class 质因数分解 {

    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int T=sc.nextInt();
        for (int k = 0; k < T; k++) {
            int n=sc.nextInt();
            System.out.print(n+"=");
            int i = 2;
            boolean j = false; //下面的 j 只是为了输出格式的控制。
            while(i*i <= n) {
                while(n%i==0) {
                    if(!j)
                        System.out.print(i);
                    else
                        System.out.print("*"+i);
                    n/=i;
                    j = true;
                }
                i++;
            }
            if(n!=0&&j==false){
                System.out.print(n);
            }else if(n!=1&&j==true){
                System.out.print("*"+n);
            }
            System.out.println();
        }
    }
}

题目链接

Java中Map的遍历

HashMap是无序的(只是说不是你插入时的顺序);
LinkedHashMap是有序的(按你插入的顺序);
TreeMap 是按key排序的;

int的最大值为2的31次方 20*10^8^ 20亿

埃氏筛法

从2开始,它留着,就把这个范围内的他的倍数划掉,下一个数留着,把它的倍数划掉,下一个数留着,把它的倍数划掉,下一个数不可能是倍数,因为它的前面因子的倍数一定之前被划掉了

题目描述
求 1,2,⋯,N 中素数的个数。

输入格式
一行一个整数 N。

输出格式
一行一个整数,表示素数的个数。

输入输出样例
输入
10
输出
4

import java.util.Scanner;
public class 素数个数 {
    public static int PrimeCount(int n){
        int[] a=new int[n+1];
        int i=2;
        while(i<=n){
            if(a[i]!=0){
                i++;
                continue;
            }
            int k=2;//倍数一定是从2开始,这样不用判断i是否为素数,一倍不会被标记,如果不是素数,前面一定被标记过,因为因子一定小于它
            int j=i;//j为临时值
            while(k*j<=n){
                a[k*j]=-1;
                k++;
            }
            i++;
        }
        int count=0;
        for (int j = 2; j <a.length ; j++) {
            if (a[j]==0){
                count++;
            }
        }
        return count;
    }
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        System.out.println(PrimeCount(n));
    }
}

斐波那契与矩阵幂运算

$$
F_{n}=\left{\begin{array}{ll}
0, & n=0 \
1, & n=1 \
F_{n-1}+F_{n-2}, & n \geq 2
\end{array}\right.
$$

$$
F_{n}=\frac{1}{\sqrt{5}}\left[\left(\frac{1+\sqrt{5}}{2}\right)^{n}-\left(\frac{1-\sqrt{5}}{2}\right)^{n}\right]
$$

$$
\left[\begin{array}{c}
F_{n} & F_{n+1} \
\end{array}\right]=\left[\begin{array}{ll}
0 & 1 \
1 & 1
\end{array}\right]^{n-1}\left[\begin{array}{c}
F_{1} & F_{2} \
\end{array}\right]
$$

详细文章

public static long fib(long n) {//矩阵运算求解斐波那契
  if (n == 1 || n == 2) return 1;
  long[][] matrix = {
      {0, 1},
      {1, 1}};
  long[][] res = Util.matrixPower(matrix, n - 1);//乘方
  res = Util.matrixMultiply(new long[][]{{1, 1}}, res);//矩阵相乘
  return res[0][0];
}
//求矩阵matrix的p次方
public static long[][] matrixPower(long[][] matrix, long p) {
  //初始化结果为单位矩阵,对角线为1
  long[][] result = new long[matrix.length][matrix[0].length];
  //单位矩阵,相当于整数的1
  for (int i = 0; i < result.length; i++) {
    result[i][i] = 1;
  }

  //平方数
  long[][] pingFang = matrix; // 一次方
  while (p != 0) {
    if ((p & 1) != 0) { // 当前二进制位最低位为1,将当前平方数乘到结果中
      result = matrixMultiply(result, pingFang);//
    }
    //平方数继续上翻
    pingFang = matrixMultiply(pingFang, pingFang);
    p >>= 1;
  }
  return result;
}

猜你喜欢

转载自www.cnblogs.com/cxynb/p/12527672.html