第一题:八皇后改
问题描述
规则同8皇后问题,但是棋盘上每格都有一个数字,要求八皇后所在格子数字之和最大。
输入格式
一个8*8的棋盘。
输出格式
所能得到的最大数字和
样例输入
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
48 50 51 52 53 54 55 56
57 58 59 60 61 62 63 64
样例输出
260
数据规模和约定
棋盘上的数字范围0~99
import java.util.Scanner;
public class Main {
private static int maxSum; // 最大数字和
public static void main(String[] args) {
int[][] board = new int[8][8];
Scanner sc = new Scanner(System.in);
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
board[i][j] = sc.nextInt();
}
}
backtrack(board, 0, new int[8]);
System.out.println(maxSum);
}
/**
* 回溯函数,按照行进行选择
*
* @param board 棋盘
* @param row 当前处理的行号
* @param cols 记录每行皇后所在的列号
*/
private static void backtrack(int[][] board, int row, int[] cols) {
if (row == 8) { // 找到一组解,比较更新数字和最大值maxSum
int sum = 0;
for (int i = 0; i < cols.length; i++) {
sum += board[i][cols[i]];
}
maxSum = Math.max(maxSum, sum);
return;
}
for (int col = 0; col < 8; col++) { // 遍历每个格子(当前行的每一列)
if (isLegal(cols, row, col)) { // 判断该位置是否可以放置皇后
cols[row] = col;
backtrack(board, row + 1, cols); // 向下一行进行选择
cols[row] = -1;
}
}
}
/**
* 判断该位置是否可以放置皇后
*
* @param cols 数组,indexes of cols
* @param row 当前要放置的行号
* @param col 当前要放置的列号
*/
private static boolean isLegal(int[] cols, int row, int col) {
for (int i = 0; i < row; i++) { // 遍历之前所有已经放置的行
if (cols[i] == col || Math.abs(i - row) == Math.abs(cols[i] - col)) { // 判断当前位置是否合法(列不能相同,斜线上也不能有重复)
return false;
}
}
return true; // 表示当前位置合法
}
}
第二题:线段长度
问题描述
给出n个线段以及它们的左端点和右端点。我们要求得到这些线段覆盖部分的长度。如线段[1,2]和[2,3]覆盖了数轴上1到3这个部分,所以它们覆盖的长度就是2。
输入格式
第一行一个数n表示有n条线段,之后的n行每行两个整数表示每个线段的左端点和右端点。
输出格式
一个数表示覆盖部分的长度。
样例输入
3
1 2
2 3
4 5
样例输出
3
数据规模和约定
0<n<=1000, 答案不超过32位整数。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] segments = new int[n][2];
for (int i = 0; i < n; i++) {
segments[i][0] = sc.nextInt();
segments[i][1] = sc.nextInt();
}
Arrays.sort(segments, (a, b) -> a[0] - b[0]);
int res = 0;
int left = segments[0][0], right = segments[0][1];
for (int i = 1; i < n; i++) {
if (segments[i][0] <= right) { // 有重叠部分
right = Math.max(right, segments[i][1]); // 扩展右端点
} else { // 没有重叠部分
res += right - left; // 更新覆盖长度
left = segments[i][0]; // 更新左右端点
right = segments[i][1];
}
}
res += right - left; // 最后还要再更新一次覆盖长度
System.out.println(res);
}
}
思路:
先按照线段的左端点从小到大排序,然后从第二个线段开始遍历,如果当前线段与前一个线段有重叠,就更新右端点;否则就将前一个线段的覆盖长度加入结果中,并更新左右端点。最后还要再加上最后一个线段的覆盖长度。
时间复杂度:O(nlogn),其中n为线段数量。
第三题:luck
问题描述
S国是一个爱好数字的国度,总有些人会夜以继日的数着数字以占卜凶吉,凡是数字在十进制表示之下有连续k个4或k个7相连则被认为是幸运的。他们看遍了1到n之间的所有数,想考一考你。因为他们预先知道答案,所以他们只需你告诉他们幸运的数字mod 109+7就行。
输入描述
第一行一个数k。
第二行一个数n。
输出描述
一个数,如题所述。
样例输入
1
100
样例输出
36
数据规模和约定
设t为n的位数
20%的数据n<=106
40%的数据n<=1018
60%的数据t<=5000
100%的数据1<=k<=t<=106
import java.util.*;
public class Main {
static final int MOD = 1000000007;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int k = sc.nextInt(); //连续k个4或k个7
long n = sc.nextLong(); //1到n之间的所有数
long[][] dp = new long[(int) (n + 1)][k + 1]; //dp[i][j]表示数字i放置了j个'4'或'7'
for (int i = 0; i <= k; i++) {
dp[0][i] = 1; //数字0不放任何'4'或'7',只有一种情况
}
long res = 0;
for (long i = 1; i <= n; i++) {
res += dfs(dp, i, k); //计算当前数字在放置了k个'4'或'7'时的方案数,并累加到结果上
res %= MOD;
}
System.out.println(res);
}
private static long dfs(long[][] dp, long num, int k) {
if (num == 0) { //递归终止条件:数字为0,只有一种情况,即不放任何'4'或'7'
return 1;
}
if (dp[(int) num][k] != 0) { //如果已经计算过此状态,则直接返回结果
return dp[(int) num][k];
}
long tmpNum = num / 10;
int lastDigit = (int) (num % 10);
long res = 0;
if (lastDigit == 4 || lastDigit == 7) { //如果当前数字的个位是'4'或'7',则递归计算上一位数字已经放置了k-1个'4'或'7'
if (k > 0) {
res += dfs(dp, tmpNum, k - 1);
res %= MOD;
}
}
res += dfs(dp, tmpNum, k); //递归计算上一位数字已经放置了k个'4'或'7'
dp[(int) num][k] = res; //记录此状态的结果
return res;
}
}
该问题可以通过动态规划+递归来解决。设dp[i][j]表示数字i放置了j个'4'或'7'时的方案数。对于一个数字num,其包含的所有合法方案总数等于:在num%10为4或7的情况下,num/10放置了j-1个4或7的方案数 + num/10放置了j个4或7的方案数。
需要注意的是,在计算num%10为4/7时,需要判断j是否大于0,因为当j=0时表示不能再放置任何4或7。同时,为了避免重复计算,使用一个二维数组dp来存储已经计算过的状态结果。
第四题:最大区间和(最大子序列和问题)
问题描述
给定n个数A1,A2,A3……An,求两个数l,r满足l<=r并最大化Al+A(l+1)+……Ar,输出这个最大值
输入格式
第一行一个数n,接下来一行有n个用空格隔开的数,第i个数表示Ai
输出格式
输出仅一行,即最大区间和
样例输入
4
-1 -2 -3 4
样例输出
4
数据规模和约定
每个数绝对值不超过2^30;
n<=1000000
package 备赛课.动态规划;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class 最大区间和 {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter pr = new PrintWriter(System.out);
int n = Integer.parseInt(br.readLine());
String a[] = br.readLine().split(" ");
long [] f = new long [n]; //存储以每个位置为结尾的最大子段和
f[0] = Integer.parseInt(a[0]);
for (int i = 1; i < n; i++) {
f[i] = Math.max(f[i - 1] + Long.parseLong(a[i]), Long.parseLong(a[i]));
}
long res = Long.MIN_VALUE;
for (int i = 0; i < n; i++) { //找到所有f中的最大值
res = Math.max(res, f[i]);
}
pr.println(res);
pr.flush();
pr.close();
}
}
第五题:质因子个数
package 算法提高;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class 质因数个数 {
static BufferedReader bu = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pr = new PrintWriter(System.out);
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
long n = Long.parseLong(bu.readLine());
long bak = n ,count=0,sum=1;
for(long i = 2 ; i*i<=n;i++) {
if(bak%i==0) {
count++;
while(bak%i==0) {
System.out.print(i+" ");
bak/=i;
}
}
}
if(bak>1) {
pr.println(++count);
pr.flush();
return;
}
pr.println(count);
pr.flush();
}
}