- 大数问题:
- 使用类库中的 BigInteger与 BigDecimal
- 例如特训题中的大数问题
- 有理数表示:
- 使用String工具类的String.format("%.2f", 7.335334) 输出7.34
- 进制问题
- 类库中的Integer的方法
- 以任意进制输入 parseInt(String s, int radix)
- 以任意进制输出 Integer.toString(n,2)
- 类库中的Integer的方法
[4.1 奇怪的捐赠]
地产大亨Q先生临终的遗愿是:拿出100万元给X社区的居民抽奖,以稍慰藉心中愧疚。
麻烦的是,他有个很奇怪的要求:
1. 100万元必须被正好分成若干份(不能剩余)。
每份必须是7的若干次方元。
比如:1元, 7元,49元,343元,...
2. 相同金额的份数不能超过5份。
3. 在满足上述要求的情况下,分成的份数越多越好!
请你帮忙计算一下,最多可以分为多少份?
转七进制
package 数论;
public class 奇怪的捐赠
{
public static void main(String[] args)
{
String string=Integer.toString(1000000,7);
int sum=0;
for (int i = 0; i < string.length(); i++)
{
sum+=Integer.parseInt(string.charAt(i)+"");
}
System.out.println(sum);
}
}
- 整数与整除问题
尼姆堆
有3堆硬币,分别是3,4,5
二人轮流取硬币。
每人每次只能从某一堆上取任意数量。
不能弃权。
取到最后一枚硬币的为赢家。
求先取硬币一方有无必胜的招法。
模2的加法,一但达到二,就对二去摸 也就是异或
1+1=0, 1+0=1, 0+1=1, 0+0=0
证明..如果和为0,无论怎么拿,都会!=0
如果非0,总有办法...0
package 数论;
public class 尼姆堆
{
/*
* 10
* 101
* 1100
* 1110
* ------ 模2的加法 结果是0 剩给谁谁输
* 0101
*/
public static void main(String[] args)
{
int []a={2,5,12,14};// 10 101 1100 1110
f(a);
}
private static void f(int[] a)
{
int sum=0;
for (int i = 0; i < a.length; i++)
{
sum^=a[i];
}
if (sum==0)
{
System.out.println("必输");
return ;//必输
}
// System.out.println(sum^a[a.length-1]);
for (int i = 0; i < a.length; i++)
{
int x=sum^a[i];
if (x<a[i])
{//不能超过堆的大小
System.out.println(a[i]+"->"+x);
}
}
}
}
-
欧几里得定理扩展
算术基本定理 质因数分解的唯一性
求最大公约数与最小公倍数
有两个数 a b,现在,我们要求 a b 的最大公约数,怎么求?枚举他们的因子?不现实,当 a b 很大的时候,枚举显得那么的naïve ,那怎么做?
最大公约数:
欧几里德有个十分又用的定理:
gcd(a, b) = gcd(b , a%b)
道理:一个数能除开A B ,也就能除开 A-B A+B
最小公倍数:
(A*B)/最大公约数gcd
Biginteger就有
[4.2 真题:天平称重]
用天平称重时,我们希望用尽可能少的砝码组合称出尽可能多的重量。
如果只有5个砝码,重量分别是1,3,9,27,81
则它们可以组合称出1到121之间任意整数重量(砝码允许放在左右两个盘中)。
本题目要求编程实现:对用户给定的重量,给出砝码组合方案。
例如:
用户输入:
5
程序输出:
9-3-1
用户输入:
19
程序输出:
27-9+1
要求程序输出的组合总是大数在前小数在后。
可以假设用户的输入的数字符合范围1~121。
package 数论;
import java.util.Scanner;
public class 天平称重
{
static int n;
private static Scanner scanner;
public static void main(String[] args)
{
// scanner = new Scanner(System.in);
// n=scanner.nextInt();
// int x[]={1,3,9,27,81};
// f(x,0,0,"");
f2(19);
}
private static void f2(int i)
{
int a=i%3;
if (i==0)
{
return;
}
if (a==2)
{
f2((i+1)/3);
a=-1;
}
else {
f2(i/3);
}
System.out.print(a);
}
private static void f(int[] x, int i,int count,String string)
{
if (i==x.length)
{
if (count==n)
{
if (string.charAt(0)=='+')
{
string=string.substring(1);
}
System.out.println(string);
}
return;
}
for (int j = -1; j <=1; j++)
{
if (j==-1)
{
f(x, i+1, count+j*x[i], new String(j*x[i]+string));
}
if (j==0)
{
f(x, i+1, count+j*x[i], new String(string));
}
if (j==1)
{
f(x, i+1, count+j*x[i], new String("+"+(j*x[i])+string));
}
}
}
}
[4.4 真题:一步之遥]
从昏迷中醒来,小明发现自己被关在X星球的废矿车里。
矿车停在平直的废弃的轨道上。
他的面前是两个按钮,分别写着“F”和“B”。
小明突然记起来,这两个按钮可以控制矿车在轨道上前进和后退。
按F,会前进97米。按B会后退127米。
透过昏暗的灯光,小明看到自己前方1米远正好有个监控探头。
他必须设法使得矿车正好停在摄像头的下方,才有机会争取同伴的援助。
或许,通过多次操作F和B可以办到。
矿车上的动力已经不太足,黄色的警示灯在默默闪烁...
每次进行 F 或 B 操作都会消耗一定的能量。
小明飞快地计算,至少要多少次操作,才能把矿车准确地停在前方1米远的地方。
请填写为了达成目标,最少需要操作的次数。
不定方程 97X+127Y=1
Ax +B y=1
扩展欧几里得定理 Ax+By=gcd(A,B)有解
同理为 Ax+By=gcd(B,A%B)
A=(A/B)*B + A % B //除数+余数
就得 ((A/B)*B + A % B)x +B y=gcd(B,A%B)
B( (A/B) (x+y) )+(A % B)x=gcd(B,A%B)
令
( (A/B) (x+y) )=x1
x=y1
B x1+A%B y1 =gcd(B,A%B)
可得
x=y1
y=x1-A/B*y1
package 数论;
public class 欧几里得最大公约数最小公倍数定理及其扩展
{
public static void main(String[] args)
{
// System.out.println(gcd(40, 20));
// System.out.println(lcm(50, 20));
int A=97;int B=127;
int []xy=new int [2];
e_gcd(A,B,xy);
System.out.println(xy[0]+" "+xy[1]);
}
/*
* 欧几里得扩展 解不定方程
* 不定方程 A * X+B * Y = gcd(A,B)
* = gcd(B,A%B)
* ((A/B)*B + A % B)x +B y
* B* (A/B * x + y )+(A%B) * x=gcd(B,A%B)
* x=新y
* y=新x-A/B*新y
*/
private static int e_gcd(int a, int b, int[] xy)
{
if (b==0)
{
xy[0]=1;
xy[1]=0;
return a;
}
int ans=e_gcd(b, a%b, xy);
int temp=xy[0];
xy[0]=xy[1];//x=新y
xy[1]=temp-a/b*xy[0];//y=新x-A/B*新y
return ans;
}
//最小公倍数: (A*B)/最大公约数
public static int lcm(int a,int b)
{
return (a*b)/gcd(a, b);
}
//欧几里德有个十分又用的定理: gcd(a, b) = gcd(b , a%b)
public static int gcd(int a,int b)
{
if (b==0)
{
return a;
}
return gcd(b, a%b);
}
}
4.6 作业:素数表
素数的筛法
第1个素数是2,第2个素数是3,...
求第100002(十万零二)个素数
package 数论;
public class 筛选法求素数
{
public static void main(String[] args)
{
int N=1500*1000;
int x=100002;
byte arr[]=new byte[N];
for (int i = 2; i < N/2; i++)
{
if (arr[i]==1)
{
continue;
}
for (int j = 2; j <= N/i; j++)
{
if (i*j<N)
{
arr[i*j]=1;
}
}
}
int count=0;
for (int i = 2; i < arr.length; i++)
{
if (arr[i]!=1)
{
count++;
if (count==x)
{
System.out.println(i);
}
}
}
System.out.println(count);
}
}
大数学家高斯有个好习惯:无论如何都要记日记。
他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210
后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?
高斯出生于:1777年4月30日。
在高斯发现的一个重要定理的日记上标注着:5343,因此可算出那天是:1791年12月15日。
高斯获得博士学位的那天日记上标着:8113
请你算出高斯获得博士学位的年月日。
A-B = A - 基准 - (B - 基准)
package 数论;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class 日期差问题
{
// 2015-3-2 距离 1979-12-15多少天
/**
* 1 将日期格式的字符串转成Date对象
* 2 将Date对象转成毫秒值
* 3 相减变成天数
*
* @param args
* @throws ParseException
*/
public static void main(String[] args) throws ParseException
{
test("2015-3-2", "1979-12-15");
}
private static void test(String str_date1, String str_date2) throws ParseException
{
// 1 将日期字符串转成日期对象。
// 定义日期格式对象。
DateFormat dateFormat = DateFormat.getDateInstance();
dateFormat = new SimpleDateFormat("yyyy-MM-dd");
//1 将日期格式的字符串转成Date对象
Date date1 = dateFormat.parse(str_date1);
Date date2 = dateFormat.parse(str_date2);
//2 将Date对象转成毫秒值
long time1 = date1.getTime();
long time2 = date2.getTime();
long time = Math.abs(time2 - time1);
//3 相减变成天数
int day = getDay(time);
System.out.println(day);
}
private static int getDay(long time)
{
int day = (int) (time / 1000 / 60 / 60 / 24);
return day;
}
}