Java输出小明算对多少题目_2014年Java方向C组第十题

标题:矩阵翻硬币

小明先把硬币摆成了一个 n 行 m 列的矩阵。

随后,小明对每一个硬币分别进行一次 Q 操作。

对第x行第y列的硬币进行 Q 操作的定义:将所有第 ix 行,第 jy 列的硬币进行翻转。

其中i和j为任意使操作可行的正整数,行号和列号都是从1开始。

当小明对所有硬币都进行了一次 Q 操作后,他发现了一个奇迹——所有硬币均为正面朝上。

小明想知道最开始有多少枚硬币是反面朝上的。于是,他向他的好朋友小M寻求帮助。

聪明的小M告诉小明,只需要对所有硬币再进行一次Q操作,即可恢复到最开始的状态。然而小明很懒,不愿意照做。于是小明希望你给出他更好的方法。帮他计算出答案。

【数据格式】

输入数据包含一行,两个正整数 n m,含义见题目描述。

输出一个正整数,表示最开始有多少枚硬币是反面朝上的。

【样例输入】

2 3

【样例输出】

1

【数据规模】

对于10%的数据,n、m <= 10^3;

对于20%的数据,n、m <= 10^7;

对于40%的数据,n、m <= 10^15;

对于100%的数据,n、m <= 10^1000(10的1000次方)。

资源约定:

峰值内存消耗(含虚拟机) < 256M

CPU消耗 < 2000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。

注意:主类的名字必须是:Main,否则按无效代码处理。

解析:

方案一:

题目中提示:“聪明的小M告诉小明,只需要对所有硬币再进行一次Q操作,即可恢复到最开始的状态。”

即假设所有格子硬币都是正面的,反向进行上述分析的模拟。

Scanner input = new Scanner(System.in);

int n = input.nextInt(); //硬币矩阵n行

int m = input.nextInt(); //硬币矩阵m列

int[][] arr = new int[n+1][m+1]; //定义硬币(数组大小+1的原因和题意相符,下标从1开始)

//将所有硬币变成正面(1:正面;0:反面)

for (int x = 1; x <= n; x++) {

for (int y = 1; y <= m; y++) {

arr[x][y] = 1;

}

}

//模拟Q操作

for (int x = 1; x <= n; x++) {

for (int y = 1; y <= m; y++) {

//循环行号和列好的倍数

for (int i = 1; i*x <= n; i++) {

for (int j = 1; j*y<=m; j++) {

int temp = arr[i*x][j*y];

arr[i*x][j*y] = temp==1?0:1;

}

}

}

}

int count = 0; //假设反面的数量为0

for (int x = 1; x <= n; x++) {

for (int y = 1; y <= m; y++) {

if(arr[x][y] == 0)

count++;

}

}

System.out.println(count);

由于题目有专门的描述如下:

【数据规模】

对于10%的数据,n、m <= 10^3;

对于20%的数据,n、m <= 10^7;

对于40%的数据,n、m <= 10^15;

对于100%的数据,n、m <= 10^1000(10的1000次方)。

资源约定:

峰值内存消耗(含虚拟机) < 256M

CPU消耗 < 2000ms

则表示此题对执行效率有一定的要求,而进行方案一进行模拟的形式,如果数据量大的情况下,效率低,不能完全满足题目的要求。

方案二:

假设硬币举证如下,模拟所有硬币Q操作翻转过程如下:

1 2 3

4 5 6

7 8 9

对第一个硬币Q操作,需要翻转硬币:1,2,3,4,5,6,7,8,9(即全部)

对第二个硬币Q操作,需要翻转硬币:2,5,8

对第三个硬币Q操作,需要翻转硬币:3,6,9

对第四个硬币Q操作,需要翻转硬币:4,5,6

对第五个硬币Q操作,需要翻转硬币:5

对第六个硬币Q操作,需要翻转硬币:6

对第七个硬币Q操作,需要翻转硬币:7,8,9

对第八个硬币Q操作,需要翻转硬币:8

对第九个硬币Q操作,需要翻转硬币:9

思路:

(1)每个硬币,如果知道被翻转了几次,则可以求出该硬币初始状态是正面还是反面,翻转了奇数次硬币初始为反面,翻转了偶数次硬币为正面。

(2)通过上述数据得出规律,翻转次数=行号的约数个数*列号的约数个数,即:

第一个硬币,行号为1(约数1,只有1个),列号为1(约数1,只有1个),翻转数量=1*1=1

第二个硬币,行号为1(约数1,只有1个),列号为2(约数1,2,有2个),翻转数量=1*2=2

第三个硬币,行号为1(约数1,只有1个),列号为3(约数1,3,有2个),翻转数量=1*2=2

第四个硬币,行号为2(约数1,2,有2个),列号为1(约数1,有1个),翻转数量=2*1=2

第五个硬币,行号为2(约数1,2,有2个),列号为2(约数1,2,有2个),翻转数量=2*2=4

第六个硬币,行号为2(约数1,2,有2个),列号为3(约数1,3,有2个),翻转数量=2*2=4

第七个硬币,行号为3(约数1,3,有2个),列号为1(约数1,有1个),翻转数量=2*1=2

第八个硬币,行号为3(约数1,3,有2个),列号为2(约数1,2,有2个),翻转数量=2*2=4

第九个硬币,行号为3(约数1,3,有2个),列号为3(约数1,3,有2个),翻转数量=2*2=4

(3)实际上不需要求行号和列号的约数个数,只需要确定约数个数是奇数还是偶数即可,如何确定某个数字的约数个数是奇数还是偶数,有规律:平方数的约数个数是奇数,其它数字的约数个数为偶数,例如:

4约数(1,2,4)有三个为奇数;9的约数(1,3,9)有三个为奇数;16约数(1,2,4,8,16)有5个为奇数

5约束(1,5)有两个为偶数;10约束(1,2,5,10)有四个为偶数;15约数(1,3,5,15)有四个为偶数

(4)翻转次数=行号的约数个数*列号的约数个数;并且(奇乘奇=奇,奇乘偶=偶,偶乘偶=偶),当结果为奇数时,初始状态才是反面,则要求行号约数个数和列号的约数个数都为奇数,则要求行号和列号都为平方数。

(5)即题目含义变成了求行号范围内有几个平方数,列号范围内有几个平方数,结果为两个数字的乘积。

(6)如果循环遍历行号和列号求含有平方数的个数,效率仍然很低下,有规律:某个数字内平方数的个数等于该数字的开方取整,例如:

9里面(1,4,9)三个平方数,9的开方也等于三;

20里面(1,4,9,16)四个平方数,20的开方然后取整等于四。

(7)我们得到结论:初始为反面的个数=行号的开方+列号的开方。

(8)由于行号和列号范围可以为(10的1000次方),直接用数字类型无法表示,无法使用Math.sqrt进行开方,只能将数字做为字符串进行处理。

(9)当数字字符串的长度为偶数,则开方结果的长度为(本身长度/2);当数字字符串的长度为奇数,则开方结果的长度为(本身长度/2+1),例如:

99长度为2,开方取整为9,长度为1;100长度为3,开方结果为10,长度为2;

(10)确定了开方长度之后,利用BigInteger和char[]数组,确定数组每一位数字求出开方结果。

public static void main(String[] args) {

Scanner input = new Scanner(System.in);

String n = input.next(); //硬币矩阵n行

String m = input.next(); //硬币矩阵m列

BigInteger rowSqrt = sqrt(n);

BigInteger colSqrt = sqrt(m);

System.out.println(rowSqrt.multiply(colSqrt));

}

public static BigInteger sqrt(String s)

{

//数字字符串的长度为偶数,则开方结果的长度为(本身长度/2);

//当数字字符串的长度为奇数,则开方结果的长度为(本身长度/2+1)

int len = s.length() % 2 == 0 ? s.length()/2 : s.length()/2+1; //求开方之后的字符串长度

char[] arr = new char[len]; //定义字符数组用于存放开方后的字符串

Arrays.fill(arr,'0'); //将字符数组所有元素设置为'0'

BigInteger num = new BigInteger(s); //将字符串转成BigInteger类型

for (int i = 0; i < arr.length; i++)

{

//循环确定字符数组每一位的数字

for (char c = '0'; c <= '9'; c++)

{

arr[i] = c;

BigInteger sqrtNum = new BigInteger(String.valueOf(arr));

if(sqrtNum.pow(2).compareTo(num) == 1) //当前假设数字的平方超过了数字本身,则减1操作

{

arr[i] -= 1;

break;

}

}

}

return new BigInteger(String.valueOf(arr));

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值