P1601 A+B Problem(高精)
题目描述
高精度加法,相当于 a+b problem,不用考虑负数。
输入格式
分两行输入。a,b≤10500。
输出格式
输出只有一行,代表 a+b 的值。
输入输出样例
输入 #1复制
1 1输出 #1复制
2输入 #2复制
1001 9099输出 #2复制
10100说明/提示
20% 的测试数据,0≤a,b≤109;
40% 的测试数据,0≤a,b≤1018。
import java.math.BigInteger;
import java.util.*;
public class Main {
public static void main(String args[]) {
Scanner in=new Scanner(System.in);
BigInteger a=new BigInteger(in.nextBigInteger()+"");
BigInteger b=new BigInteger(in.nextBigInteger()+"");
System.out.println(a.add(b));
}
}
BigInteger
的用法
BigInteger
类用于处理任意大小的整数,它可以处理比int
和long
类型所能表示的范围大得多的整数。1. 导入
BigInteger
类收起
java
import java.math.BigInteger;
2. 创建
BigInteger
对象
- 使用字符串构造:最常用的方式,通过传入一个表示整数的字符串来创建
BigInteger
对象。收起
java
BigInteger num1 = new BigInteger("12345678901234567890");
- 使用静态常量:
BigInteger
类提供了一些静态常量,如ZERO
、ONE
和TEN
。收起
java
BigInteger zero = BigInteger.ZERO; BigInteger one = BigInteger.ONE; BigInteger ten = BigInteger.TEN;
3. 基本运算
- 加法:使用
add
方法。收起
java
BigInteger resultAdd = num1.add(one);
- 减法:使用
subtract
方法。收起
java
BigInteger resultSubtract = num1.subtract(one);
- 乘法:使用
multiply
方法。收起
java
BigInteger resultMultiply = num1.multiply(ten);
- 除法:使用
divide
方法。收起
java
BigInteger resultDivide = num1.divide(ten);
- 取模:使用
mod
方法。收起
java
BigInteger resultMod = num1.mod(ten);
4. 比较大小
使用
compareTo
方法,返回值为 -1(小于)、0(等于)或 1(大于)。收起
java
int comparison = num1.compareTo(one);
BigDecimal
的用法
BigDecimal
类用于处理任意精度的十进制数,常用于需要高精度计算的金融领域。1. 导入
BigDecimal
类收起
java
import java.math.BigDecimal;
2. 创建
BigDecimal
对象
- 使用字符串构造:推荐使用这种方式,避免使用
double
类型构造时可能出现的精度问题。收起
java
BigDecimal num2 = new BigDecimal("123.456");
- 使用
double
或int
构造:不推荐用于高精度计算,因为double
类型存在精度问题。收起
java
BigDecimal num3 = new BigDecimal(123.456);
3. 基本运算
- 加法:使用
add
方法。收起
java
BigDecimal resultAddDecimal = num2.add(new BigDecimal("1.23"));
- 减法:使用
subtract
方法。收起
java
BigDecimal resultSubtractDecimal = num2.subtract(new BigDecimal("1.23"));
- 乘法:使用
multiply
方法。收起
java
BigDecimal resultMultiplyDecimal = num2.multiply(new BigDecimal("2.0"));
- 除法:使用
divide
方法,需要指定精度和舍入模式。收起
java
BigDecimal resultDivideDecimal = num2.divide(new BigDecimal("3.0"), 2, BigDecimal.ROUND_HALF_UP);
4. 设置精度和舍入模式
divide
方法需要指定精度和舍入模式,常见的舍入模式有:
ROUND_HALF_UP
:四舍五入。ROUND_DOWN
:直接舍去。ROUND_UP
:直接进位。
P1249 最大乘积
题目描述
一个正整数一般可以分为几个互不相同的自然数的和,如 3=1+2,4=1+3,5=1+4=2+3,6=1+5=2+4。
现在你的任务是将指定的正整数 n 分解成若干个互不相同的自然数(也可以不分解,就是这个数字本身)的和,且使这些自然数的乘积最大。
输入格式
只一个正整数 n,(3≤n≤10000)。
输出格式
第一行是分解方案,相邻的数之间用一个空格分开,并且按由小到大的顺序。
第二行是最大的乘积。
输入输出样例
输入 #1复制
10输出 #1复制
2 3 5 30
import java.util.Scanner;
public class Main {
// 定义数组存储拆分的数和乘积结果
static int[] a = new int[10001];
static int[] s = new int[10001];
// 输入的数 n 和乘积结果的长度
static int n;
static int len = 1;
// 乘法函数,模拟大整数乘法
public static void mul(int x) {
// 每一位乘以 x
for (int i = 1; i <= len; i++) {
s[i] *= x;
}
// 处理进位
for (int i = 1; i <= len; i++) {
s[i + 1] += s[i] / 10;
s[i] %= 10;
}
// 如果最高位还有进位,增加长度并继续处理进位
while (s[len + 1] > 0) {
len++;
s[len + 1] += s[len] / 10;
s[len] %= 10;
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取输入的数 n
n = scanner.nextInt();
scanner.close();
// 处理特殊情况 n = 3 和 n = 4
if (n == 3) {
System.out.println(3);
System.out.println(3);
return;
}
if (n == 4) {
System.out.println(4);
System.out.println(4);
return;
}
// 初始化乘积结果
s[0] = s[1] = 1;
// 拆分和累加的和以及拆分的数的个数
int sum = 0;
int tot = 0;
// 将 n 拆分为从 2 开始的连续自然数的和
for (int i = 2; sum < n; i++) {
a[++tot] = i;
sum += i;
}
// 根据拆分结果进行调整
if (sum > n + 1) {
a[sum - n - 1] = 0;
} else if (sum == n + 1) {
a[tot]++;
a[1] = 0;
}
// 输出拆分的数并计算乘积
for (int i = 1; i <= tot; i++) {
if (a[i] != 0) {
System.out.print(a[i] + " ");
mul(a[i]);
}
}
System.out.println();
// 输出乘积结果
for (int i = len; i >= 1; i--) {
System.out.print(s[i]);
}
System.out.println();
}
}
观察性质
先举出几个简单的例子:
- 5→2×3
- 6→2×4
- 9→2×3×4
- 10→2×3×5
我们观察到,大部分分解出来的数都是较为连续的。
得出结论
考虑这样一种贪心策略:
首先构造出连续一段自然数,使得和恰好大于或等于 n ,然后找到一个合适的数并更改(如果等于 n 就不更改),使得和满足要求。
例如分解一个数 15 :
- 首先找到连续一段自然数: 2+3+4+5+6>15 (为什么要从 2 开始而不是 3 或更大?请思考);
- 发现 2+3+4+5+6=20 ,恰好与 15 相差 5 ;
- 把 5 删掉,得到 2+3+4+6=15 ,计算答案。
若找到的和与 n 相差 1 ,可以直接删除 2 并将最后一个数加上 1 。
注意:当 n=3,4 时并不符合上述的贪心策略,需要特判。
lna+lnb=ln(a×b) dp的方法
P4924 [1007] 魔法少女小Scarlet
题目描述
Scarlet 最近学会了一个数组魔法,她会在 n×n 二维数组上将一个奇数阶方阵按照顺时针或者逆时针旋转 90∘。
首先,Scarlet 会把 1 到 n2 的正整数按照从左往右,从上至下的顺序填入初始的二维数组中,然后她会施放一些简易的魔法。
Scarlet 既不会什么分块特技,也不会什么 Splay 套 Splay,她现在提供给你她的魔法执行顺序,想让你来告诉她魔法按次执行完毕后的二维数组。
输入格式
第一行两个整数 n,m,表示方阵大小和魔法施放次数。
接下来 m 行,每行 4 个整数 x,y,r,z,表示在这次魔法中,Scarlet 会把以第 x 行第 y 列为中心的 2r+1 阶矩阵按照某种时针方向旋转,其中 z=0 表示顺时针,z=1 表示逆时针。
输出格式
输出 n 行,每行 n 个用空格隔开的数,表示最终所得的矩阵
输入输出样例
输入 #1复制
5 4 2 2 1 0 3 3 1 1 4 4 1 0 3 3 2 1输出 #1复制
5 10 3 18 15 4 19 8 17 20 1 14 23 24 25 6 9 2 7 22 11 12 13 16 21说明/提示
对于50%的数据,满足 r=1
对于100%的数据 1≤n,m≤500,满足 1≤x−r≤x+r≤n,1≤y−r≤y+r≤n。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 定义二维数组 g 用于存储原始矩阵,f 作为临时数组
int[][] g = new int[510][510];
int[][] f = new int[510][510];
int tot = 0;
// 读取矩阵的大小 n 和操作的次数 m
int n = scanner.nextInt();
int m = scanner.nextInt();
// 初始化矩阵 g,为每个元素赋值
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
g[i][j] = ++tot;
}
}
// 进行 m 次操作
for (int i = 1; i <= m; i++) {
// 读取操作的参数:中心点坐标 (a, b)、半径 r 和操作类型 opt
int a = scanner.nextInt();
int b = scanner.nextInt();
int r = scanner.nextInt();
int opt = scanner.nextInt();
if (opt == 0) {
// 顺时针旋转操作
for (int x = a - r; x <= a + r; x++) {
for (int y = b - r; y <= b + r; y++) {
f[a - b + y][a + b - x] = g[x][y];
}
}
// 将临时数组 f 的值复制回矩阵 g
for (int x = a - r; x <= a + r; x++) {
for (int y = b - r; y <= b + r; y++) {
g[x][y] = f[x][y];
}
}
} else {
// 逆时针旋转操作
for (int x = a - r; x <= a + r; x++) {
for (int y = b - r; y <= b + r; y++) {
f[a + b - y][b - a + x] = g[x][y];
}
}
// 将临时数组 f 的值复制回矩阵 g
for (int x = a - r; x <= a + r; x++) {
for (int y = b - r; y <= b + r; y++) {
g[x][y] = f[x][y];
}
}
}
}
// 输出最终的矩阵 g
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
System.out.print(g[i][j] + " ");
}
System.out.println();
}
scanner.close();
}
}
P1518 [USACO2.4] 两只塔姆沃斯牛 The Tamworth Two
题目描述
两只牛逃跑到了森林里。Farmer John 开始用他的专家技术追捕这两头牛。你的任务是模拟他们的行为(牛和 John)。
追击在 10×10 的平面网格内进行。一个格子可以是:一个障碍物,两头牛(它们总在一起),或者 Farmer John。两头牛和 Farmer John 可以在同一个格子内(当他们相遇时),但是他们都不能进入有障碍的格子。
一个格子可以是:
.
空地;*
障碍物;C
两头牛;F
Farmer John。这里有一个地图的例子:
*...*..... ......*... ...*...*.. .......... ...*.F.... *.....*... ...*...... ..C......* ...*.*.... .*.*......
牛在地图里以固定的方式游荡。每分钟,它们可以向前移动或是转弯。如果前方无障碍(地图边沿也是障碍),它们会按照原来的方向前进一步。否则它们会用这一分钟顺时针转 90 度。 同时,它们不会离开地图。
Farmer John 深知牛的移动方法,他也这么移动。
每次(每分钟)Farmer John 和两头牛的移动是同时的。如果他们在移动的时候穿过对方,但是没有在同一格相遇,我们不认为他们相遇了。当他们在某分钟末在某格子相遇,那么追捕结束。
读入十行表示地图。每行都只包含 10 个字符,表示的含义和上面所说的相同。保证地图中只有一个
F
和一个C
。F
和C
一开始不会处于同一个格子中。计算 Farmer John 需要多少分钟来抓住他的牛,假设牛和 Farmer John 一开始的行动方向都是正北(即上)。 如果 John 和牛永远不会相遇,输出 0。
输入格式
输入共十行,每行 10 个字符,表示如上文描述的地图。
输出格式
输出一个数字,表示 John 需要多少时间才能抓住牛们。如果 John 无法抓住牛,则输出 0。
输入输出样例
输入 #1复制
*...*..... ......*... ...*...*.. .......... ...*.F.... *.....*... ...*...... ..C......* ...*.*.... .*.*......输出 #1复制
49说明/提示
翻译来自NOCOW
USACO 2.4
import java.util.Scanner;
public class Main {
static char[][] m = new char[12][12]; // 地图
static int[] f = new int[3]; // 农夫
static int[] c = new int[3]; // 奶牛
static int ans, tdz; // 秒数,专属值
static boolean[] zt = new boolean[200000]; // 扩大数组大小避免越界
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 初始化地图边界
for (int i = 0; i <= 11; i++) {
for (int j = 0; j <= 11; j++) {
m[i][j] = '*';
}
}
// 读取地图
for (int i = 1; i <= 10; i++) {
String line = scanner.nextLine().trim();
for (int j = 1; j <= 10; j++) {
m[i][j] = line.charAt(j - 1);
if (m[i][j] == 'F') {
f[1] = i;
f[2] = j;
}
if (m[i][j] == 'C') {
c[1] = i;
c[2] = j;
}
}
}
// 模拟每秒
while (pd()) {
tdz = f[1] + f[2] * 10 + c[1] * 100 + c[2] * 1000 + f[0] * 10000 + c[0] * 40000;
if (tdz >= zt.length || tdz < 0) { // 额外检查防止意外越界
System.out.println(0);
return;
}
if (zt[tdz]) {
System.out.println(0);
return;
}
zt[tdz] = true;
move(f[1], f[2], f[0], 0);
move(c[1], c[2], c[0], 1);
ans++;
}
System.out.println(ans);
}
static void move(int x, int y, int mi, int h) {
switch (mi) {
case 0: // 北
if (m[x - 1][y] == '*') {
if (h == 0) f[0] = 1;
else c[0] = 1;
} else {
if (h == 0) f[1]--;
else c[1]--;
}
break;
case 1: // 东
if (m[x][y + 1] == '*') {
if (h == 0) f[0] = 2;
else c[0] = 2;
} else {
if (h == 0) f[2]++;
else c[2]++;
}
break;
case 2: // 南
if (m[x + 1][y] == '*') {
if (h == 0) f[0] = 3;
else c[0] = 3;
} else {
if (h == 0) f[1]++;
else c[1]++;
}
break;
case 3: // 西
if (m[x][y - 1] == '*') {
if (h == 0) f[0] = 0;
else c[0] = 0;
} else {
if (h == 0) f[2]--;
else c[2]--;
}
break;
}
}
static boolean pd() {
return !(f[1] == c[1] && f[2] == c[2]);
}
}
P1591 阶乘数码
题目描述
求 n! 中某个数码出现的次数。
输入格式
第一行为 t(t≤10),表示数据组数。接下来 t 行,每行一个正整数 n(n≤1000) 和数码 a。
输出格式
对于每组数据,输出一个整数,表示 n! 中 a 出现的次数。
输入输出样例
输入 #1复制
2 5 2 7 0输出 #1复制
1 2
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int a[5000];
int main()
{
int t;
cin>>t;
while (t--)
{
for (int i=1;i<=1001;i++)
a[i]=0;//将数组清零。
a[1]=1;//必须设为1。不能为0,不然怎么乘都是0。
int n,i,j,k,m;
int p=1,jw=0;//p代表位数,jw代表进位。
scanf("%d%d",&n,&m);
for(i=2;i<=n;i++)//从2开始,反正任何数乘1还等于它本身。
{
jw=0;
for(j=1;j<=p;j++)//高精度*单精度。
{
a[j]=a[j]*i+jw;//高精度*单精度+进位。
jw=a[j]/10;//设置进位。
a[j]=a[j]%10;
}
while(jw>0)//如果还有进位,处理进位。
{
a[j]=jw%10;
jw/=10;
j++;
}
p=j-1;
}
long long sum=0;
for (i=p;i>=1;i--)//搜索n!里有几个指定数字。
{
if (a[i]==m)
sum++;
}
cout<<sum<<endl;//输出。
}
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int t = scanner.nextInt();
int[] a = new int[10000]; // 足够大的数组防止越界
while (t-- > 0) {
// 初始化数组
for (int i = 1; i <= 1000; i++) a[i] = 0;
a[1] = 1; // 初始值为1
int p = 1; // 当前位数
int n = scanner.nextInt();
int m = scanner.nextInt();
// 计算n!
for (int i = 2; i <= n; i++) {
int carry = 0;
// 逐位相乘
for (int j = 1; j <= p; j++) {
a[j] = a[j] * i + carry;
carry = a[j] / 10;
a[j] %= 10;
}
// 处理剩余进位
while (carry > 0) {
a[++p] = carry % 10;
carry /= 10;
}
}
// 统计数字出现次数
int count = 0;
for (int i = p; i >= 1; i--) {
if (a[i] == m) count++;
}
System.out.println(count);
}
scanner.close();
}
}
import java.math.BigInteger;
public class FactorialWithBigInteger {
public static BigInteger factorial(int n) {
// 初始化结果为 1
BigInteger result = BigInteger.ONE;
// 循环计算阶乘
for (int i = 1; i <= n; i++) {
// 将当前数转换为 BigInteger 类型
BigInteger current = BigInteger.valueOf(i);
// 将结果乘以当前数
result = result.multiply(current);
}
return result;
}
public static void main(String[] args) {
int number = 20; // 要计算阶乘的数
BigInteger factorialResult = factorial(number);
System.out.println(number + " 的阶乘是: " + factorialResult);
}
}求阶乘