深度优先

这个家伙好懒,除了文章什么都没留下

0%

历届试题 矩阵翻硬币

时间限制:1.0s 内存限制:256.0MB

问题描述

小明先把硬币摆成了一个 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;
对于10%的数据,n、m <= 10^1000(10的1000次方)。

这个题最先是想着通过模拟翻硬币的那个过程做的,但是提交后数据量非常大只过了10%的数据,然后打印出翻硬币的情况,从中发现了规律。

最终,归结为n开方数*m的开方数即为最终输出的结果。燃鹅,n和m都是非常的数,这就涉及到大数开方的算法。

模拟翻硬币的过程代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.util.Scanner;

public class 矩阵翻硬币 {

public static void main(String[] args) {

Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int m=sc.nextInt();

int count=getFan(n+1,m+1);
System.out.println(count);
}
private static int getFan(int n,int m) {

int[][] data=new int[n][m];

for (int i = 1; i < n+1; i++) {
for (int j = 1; j < m+1; j++) {
int x=i,y=j;
for (int k = 1; k*x < n; k++) {
for (int h = 1; h*y < m ; h++) {
if(data[k*x][h*y]==0)
data[k*x][h*y]=1;
else
data[k*x][h*y]=0;
}
}
}
}
int count=0;
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++) {
System.out.print(data[i][j]+",");
if(data[i][j]==1)
count++;
}
System.out.println();
}
return count;
}
}

找到规律后用n开方*m开方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.math.BigInteger;
import java.util.Scanner;

public class 大数开方2 {

public static void main(String[] args) {

Scanner sc=new Scanner(System.in);

BigInteger n=sc.nextBigInteger();
BigInteger m=sc.nextBigInteger();

System.out.println(sqrt(n).multiply(sqrt(m)));

}

private static BigInteger sqrt(BigInteger n) {
BigInteger a;
BigInteger b;

a=BigInteger.TEN.pow((int)n.toString().length()/2);
b=n.divide(a);

while(!(a.equals(b)||a.equals(b.add(new BigInteger("-1")))||b.equals(a.add(new BigInteger("-1"))))){
a=a.add(b).divide(new BigInteger("2"));//a=(a+b)/2
b=n.divide(a);
}

if (a.equals(b.add(new BigInteger("-1")))) {
return a;
}
return b;
}
}

大数开方的算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.math.BigInteger;
import java.util.Scanner;

public class 大数开方3 {
public static BigInteger getSqrt(BigInteger n){
BigInteger a,b;
BigInteger cs=new BigInteger("2");
int len=n.toString().length();
a=BigInteger.TEN.pow(len/2);
b=n.divide(a);
int k=1;
while(k==1){
if(a.equals(b)||a.equals(BigInteger.ONE.add(b))||b.equals(BigInteger.ONE.add(a))){
k=0;
}else{
a=a.add(b).divide(cs);
b=n.divide(a);
}
}
return a;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc=new Scanner(System.in);
for (int i = 1; i < 20; i++) {
BigInteger n=BigInteger.valueOf(i);
System.out.println(i+"开方为:"+getSqrt(n));
}
//BigInteger n=sc.nextBigInteger();
//System.out.println(getSqrt(n));
}

}

题目:

给定一个正整数n,求一共有多少种方式将它写成若干个正整数之和。

例如:
n=4,则输出5.因为4只有如下五种求和方式:
4 = 4
4 = 3 + 1
4 = 2 + 2
4 = 2 + 1 + 1
4 = 1 + 1 + 1 + 1

第一种方法,简单递归。
n的拆分,太复杂,但是如果我们限制了最多拆成几个整数之和,就简单些
例如
拆成一个整数:4 = 4 一种
拆成两个整数:4 = 3 + 1;4 = 2 + 2 两种……

递归(记忆化)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import java.util.Scanner;

public class 整数拆分1 {

static int[][] data;
static int p(int n, int m){

System.out.println(n+"+"+m);
if(n<m)return 0;

if(n==1||m==1||n==m) return 1;

if(n>m){
if(data[n][m]==-1)
data[n][m]=p(n-1,m-1)+p(n-m,m);
return data[n][m];
}
return 0;
}

public static void main(String[] args) {

Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
data=new int[n+1][n+1];

for (int i = 0; i < n+1; i++) {
for (int j = 0; j < n+1; j++) {
data[i][j]=-1;
}
}
int result = 0;
for(int i=1; i<=n; i++){
result += p(n, i);
}
System.out.println(result);
}
}

动态规划

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.Scanner;

public class 数的拆分dp {

static int[][] dp;
public static void main(String[] args) {

Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
dp=new int[n+1][n+1];

for (int i = 1; i < n+1; i++) {
dp[i][1]=1;
dp[i][i]=1;
for (int j = 1; j < n+1; j++) {
if(i>j){
dp[i][j]=dp[i-1][j-1]+dp[i-j][j];
}
}
for (int j = 1; j < i+1; j++) {
dp[i][n]+=dp[i][j];
}

//打表
for (int j = 0; j < n+1; j++) {
System.out.print(dp[i][j]+",");
}
System.out.println();
}
System.out.println(dp[n][n]/2);
}
}

用dfs写的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class 数的拆分 {
public static void main(String[] args) {
int[] data=new int[100];
f(6,0,data);
}

private static void f(int m, int k,int[] data) {
if(m==0)
{
for (int i = 0; i < k; i++) {
System.out.print(data[i]+" ");
}
System.out.println();
}

for (int i = m; i > 0; i--) {
if(k>0 && data[k-1]<i)continue;
data[k]=i;
f(m-i,k+1,data);
}
}
}

历届试题 地宫取宝

时间限制:1.0s 内存限制:256.0MB

问题描述

X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。

地宫的入口在左上角,出口在右下角。

小明被带到地宫的入口,国王要求他只能向右或向下行走。

走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。

当小明走到出口时,如果他手中的宝贝恰好是k件,则这些宝贝就可以送给小明。

请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这k件宝贝。

输入格式

输入一行3个整数,用空格分开:n m k (1<=n,m<=50, 1<=k<=12)

接下来有 n 行数据,每行有 m 个整数 Ci (0<=Ci<=12)代表这个格子上的宝物的价值

输出格式

要求输出一个整数,表示正好取k个宝贝的行动方案数。该数字可能很大,输出它对 1000000007 取模的结果。

样例输入

2 2 2
1 2
2 1

样例输出

2

样例输入

2 3 2
1 2 3
2 1 5

样例输出

14

类似迷宫问题,然后刚学了。只过了42%的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import java.util.Scanner;

public class Main {

static int n,m,k;
static int[][] map;
static int t=0;
public static void main(String[] args) {

Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
k=sc.nextInt();
map=new int[2+n][2+m];

for (int i = 0; i < 2+n; i++) {
for (int j = 0; j < 2+m; j++) {
if(i==0||j==0||i==1+n||j==1+m)//加一道墙
map[i][j]=-1;
else
map[i][j]=sc.nextInt();
}
}
dfs(1,1,-1,0);
dfs(1,1,map[1][1],1);
System.out.println(t);
}

//max当前拿到手中的最大个,count已经拿了多少个
private static void dfs(int i, int j,int max,int count) {

//System.out.println("("+i+","+j+"),count="+count+",max="+max);
//到达终点
if(i==n&&j==m){
if(count==k){
t=(t+1)%1000000007;
//System.out.println("OK!");
return;
}
}

if(!check(i,j)){
return;
}

//向右走
if(check(i,j+1)){
dfs(i,j+1,max,count);
//能不能拿起
if(map[i][j+1]>max){
dfs(i,j+1,map[i][j+1],count+1);//拿不拿起
}
}

//向下走
if(check(i+1,j)){
dfs(i+1,j,max,count);
if(map[i+1][j]>max){
dfs(i+1,j,map[i+1][j],count+1);
}
}

}

private static boolean check(int i, int j) {
if(map[i][j]!=-1)
return true;
else
return false;
}
}

优化后的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
 import java.util.Scanner;

/*
* 修改后的递归
*/
public class Main {

static int n,m,k;
static int[][] map;
static long[][][][] data;

public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
k=sc.nextInt();
map=new int[n+2][m+2];
data=new long[52][52][14][14];

for(int i=0;i<52;i++){
for(int ii=0;ii<52;ii++){
for(int iii=0;iii<14;iii++){
for(int iiii=0;iiii<14;iiii++){
data[i][ii][iii][iiii]=-1;
}
}
}
}

for (int i = 0; i < n+2; i++) {
for (int j = 0; j < m+2; j++) {
if(i==0||j==0||i==n+1||j==m+1)//加一道墙
map[i][j]=-1;
else
map[i][j]=sc.nextInt();
}
}

long total = dfs(1,1,-1,k); //从(1,1)开始,还可以拿k个,现在可拿">-1"的值
//这里写dfs(1,1,0,k)效果是一样的,为防止map中有质量为0的物品,写-1更稳妥
System.out.println(total%1000000007);
}

//max当前拿到手中的最大个,count还可以拿多少个
private static long dfs(int i, int j,int max,int count) {
//System.out.println("("+i+","+j+"),count="+count+",max="+max);

//无意义
if(!check(i, j, count)){
return 0;
}

//到达
if(i==n && j==m && count==0){
//System.out.println("成功!");
return data[i][j][max+1][count]=1;
}

if(i==n && j==m && count==1 && map[i][j]>max){
//System.out.println("成功!取最后一个");
return data[i][j][max+1][count]=1;
}

if(data[i][j][max+1][count]!=-1){
return data[i][j][max+1][count];
}
//如果现在的可以拿,则有四种情况:拿起,向右走;拿起,向下走;不拿,向右走;不拿向下走。
if(map[i][j]>max){
data[i][j][max+1][count]=dfs(i,j+1,map[i][j],count-1)+dfs(i+1,j,map[i][j],count-1)+dfs(i,j+1,max,count)+dfs(i+1,j,max,count);
return data[i][j][max+1][count];
}

//如果现在的不能拿,则有两种情况:不拿,向右走;不拿向下走。
if(map[i][j]<=max){
data[i][j][max+1][count]=dfs(i+1,j,max,count)+dfs(i,j+1,max,count);
return data[i][j][max+1][count];
}

return 0;
}

private static boolean check(int i, int j, int count) {
if(map[i][j]!=-1 && count>=0)
return true;
else
return false;
}
}