蓝桥杯新手算法练习题单|冲击国一(三)
此题单为算法基础精选题单,包含蓝桥杯常考考点以及各种经典算法,可以帮助你打牢基础,查漏补缺。
本题单目标是冲击蓝桥杯省一国一,团体程序天梯赛个人国三、XCPC区域赛铜/银奖
前言
本次题单重点关注模拟类问题,DFS问题,BFS问题
目录
模拟类题型
一、最大子矩阵
二、 世纪末的星期
三、图像相似度
四、操作系统
DFS题型
五 、老子的全排列呢
六、皇后问题
七、 池塘
BFS题型
八、迷宫
九、八数码问题
十、 字符变换
一、最大子矩阵
0最大子矩阵—原题链接
下面是一个 20×20 的矩阵,矩阵中的每个数字是一个 1 到 9 之间的数字,请注意显示时去除了分隔符号。
69859241839387868941
17615876963131759284
37347348326627483485
53671256556167864743
16121686927432329479
13547413349962773447
27979945929848824687
53776983346838791379
56493421365365717745
21924379293872611382
93919353216243561277
54296144763969257788
96233972513794732933
81443494533129939975
61171882988877593499
61216868895721348522
55485345959294726896
32124963318242554922
13593647191934272696
56436895944919899246
矩阵中一个子矩阵的值是指子矩阵中所有数值的和。
请问,矩阵中值最大的一个 5×5 的子矩阵的值是多少?
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner scan = new Scanner(System.in);
//在此输入您的代码...
// int arr[][]=new int[20][20];
// String s="6985924183938786894117615876963131759284373473483266274834855367125655616786474316121686927432329479135474133499627734472797994592984882468753776983346838791379564934213653657177452192437929387261138293919353216243561277542961447639692577889623397251379473293381443494533129939975611718829888775934996121686889572134852255485345959294726896321249633182425549221359364719193427269656436895944919899246";
// char c[]=s.toCharArray();
// int g=0,max=0,sum=0;
// for(int i=0;i<20;i++) {
// for(int j=0;j<20;j++) {
// arr[i][j]=c[g++]-'0';
// }
// }
// for(int i=0;i<20-4;i++) {
// for(int j=0;j<20-4;j++) {
// sum=arr[i][j]+arr[i][j+1]+arr[i][j+2]+arr[i][j+3]+arr[i][j+4]
// +arr[i+1][j]+arr[i+1][j+1]+arr[i+1][j+2]+arr[i+1][j+3]+arr[i+1][j+4]
// +arr[i+2][j]+arr[i+2][j+1]+arr[i+2][j+2]+arr[i+2][j+3]+arr[i+2][j+4]
// +arr[i+3][j]+arr[i+3][j+1]+arr[i+3][j+2]+arr[i+3][j+3]+arr[i+3][j+4]
// +arr[i+4][j]+arr[i+4][j+1]+arr[i+4][j+2]+arr[i+4][j+3]+arr[i+4][j+4];
// max=sum>max?sum:max;
// }
// }
// System.out.print(max);
System.out.print(154);
scan.close();
}
}
二、 世纪末的星期
世纪末的星期 - 蓝桥云课
本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。
曾有邪教称 1999 年 12月 31 日是世界末日。当然该谣言已经不攻自破。
还有人称今后的某个世纪末的 12月 31 日,如果是星期一则会....
有趣的是,任何一个世纪末的年份的 12月 31 日都不可能是星期一!!
于是,“谣言制造商”又修改为星期日......
1999年的 12 月 31 日是星期五,请问:未来哪一个离我们最近的一个世纪末年(即 xx99年)的 12 月 31 日正好是星期天(即星期日)?
请回答该年份(只写这个 4 位整数,不要写 12 月 31 等多余信息)
import java.util.Scanner;
import java.time.*;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
public static void main(String[] args) {
LocalDate start = LocalDate.of(1999, 12, 31);
while(true) {
if(start.getDayOfWeek().getValue()==7) {
System.out.print(start.getYear());
return;
}
start = start.plusYears(100);
}
}
}
三、图像相似度
图像相似度
给出两幅相同大小的黑白图像(用0-1矩阵)表示,求它们的相似度。若两幅图像在相同位置上的像素点颜色相同,则称它们在该位置具有相同的像素点。两幅图像的相似度定义为相同像素点数占总像素点数的百分比。
输入描述:
第一行包含两个整数m和n,表示图像的行数和列数,用单个空格隔开。1≤m≤100, 1≤n≤100。之后m行,每行n个整数0或1,表示第一幅黑白图像上各像素点的颜色,相邻两个数用单个空格隔开。之后m行,每行n个整数0或1,表示第二幅黑白图像上各像素点的颜色,相邻两个数用单个空格隔开。
输出描述:
一个实数,表示相似度(以百分比的形式给出),精确到小数点后两位。
示例1
输入
3 3 1 0 1 0 0 1 1 1 0 1 1 0 0 0 1 0 0 1
输出
44.44
import java.awt.datatransfer.SystemFlavorMap;
import java.math.BigInteger;
import java.util.Scanner;
import org.omg.PortableInterceptor.IORInterceptor;
import java.sql.*;
public class Main {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int n,m,num=0;
int[][] a = new int[105][105];
int[][] b = new int[105][105];
n=cin.nextInt();
m=cin.nextInt();
for(int i = 0; i < n; i ++){
for(int j = 0; j < m; j ++){
a[i][j]=cin.nextInt();
}
}
for(int i = 0; i < n; i ++){
for(int j = 0; j < m; j ++){
b[i][j]=cin.nextInt();
}
}
for(int i = 0; i < n; i ++){
for(int j = 0; j < m; j ++){
if(a[i][j] == b[i][j]){
num++;
}
}
}
System.out.println(String.format("%.2f",num*100.0/(n*m)));
}
}
四、操作系统
[HNOI2003]操作系统
写一个程序来模拟操作系统的进程调度。假设该系统只有一个CPU,每一个进程的到达时间,执行时间和运行优先级都是已知的。其中运行优先级用自然数表示,数字越大,则优先级越高。
如果一个进程到达的时候CPU是空闲的,则它会一直占用CPU直到该进程结束。除非在这个过程中,有一个比它优先级高的进程要运行。在这种情况下,这个新的(优先级更高的)进程会占用CPU,而老的只有等待。
如果一个进程到达时,CPU正在处理一个比它优先级高或优先级相同的进程,则这个(新到达的)进程必须等待。一旦CPU空闲,如果此时有进程在等待,则选择优先级最高的先运行。如果有多个优先级最高的进程,则选择到达时间最早的。
输入描述:
输入文件包含若干行,每一行有四个自然数(均不超过108),分别是进程号,到达时间,执行时间和优先级。不同进程有不同的编号,不会有两个相同优先级的进程同时到达。
输入数据已经按到达时间从小到大排序。输入数据保证在任何时候,等待队列中的进程不超过15000个。
输出描述:
按照进程结束的时间输出每个进程的进程号和结束时间
示例1
输入
1 1 5 3 2 10 5 1 3 12 7 2 4 20 2 3 5 21 9 4 6 22 2 4 7 23 5 2 8 24 2 4
输出
1 6 3 19 5 30 6 32 8 34 4 35 7 40 2 42
import java.io.StreamTokenizer;
import java.util.PriorityQueue;
import java.util.Comparator;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws IOException {
Scanner in = new Scanner(System.in);
PrintWriter out = new PrintWriter(System.out);
int ans = 0;
int a, b, c, d;
Node now, nxt;
PriorityQueue<Node> pq = new PriorityQueue<Node>(1, new cmp());
while(in.hasNext()) {
a = in.nextInt();
b = in.nextInt();
c = in.nextInt();
d = in.nextInt();
nxt = new Node(a, b, c, d);
while(!pq.isEmpty() && ans + pq.peek().work <= nxt.arr) {
now = pq.poll();
ans += now.work;
out.println(now.num + " " + ans);
}
if(!pq.isEmpty()) {
now = pq.poll();
now.work = now.work - nxt.arr + ans;
pq.add(now);
}
pq.add(nxt);
ans = nxt.arr;
}
while(!pq.isEmpty()) {
now = pq.poll();
ans += now.work;
out.println(now.num + " " + ans);
}
out.close();
}
static class Node {
int num, arr, work, pri;
public Node(int num, int arr, int work, int pri) {
this.num = num;
this.arr = arr;
this.work = work;
this.pri = pri;
}
}
static class cmp implements Comparator <Node> {
public int compare(Node a, Node b) {
if(a.pri == b.pri) return a.arr - b.arr;
else return b.pri - a.pri;
}
}
}
五 、老子的全排列呢
老子的全排列呢
老李见和尚赢了自己的酒,但是自己还舍不得,所以就耍起了赖皮,对和尚说,光武不行,再来点文的,你给我说出来1-8的全排序,我就让你喝,这次绝不耍你,你能帮帮和尚么?
输入描述:
无
输出描述:
1~8的全排列,按照全排列的顺序输出,每行结尾无空格。
示例1
输入
No_Input
输出
Full arrangement of 1~8
备注:
1~3的全排列 :
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
import java.util.Scanner;
public class Main{
static int N=100,n;
static int []path=new int[N];
static boolean flag[]=new boolean[N];
public static void main( String[]args ){
n=8;
dfs(0);
}
static void dfs(int u){
if(u==n){
for(int i=0;i<n;i++){
System.out.print(path[i]+" ");
}
System.out.println();
return;
}
for(int i=1;i<=n;i++){
if(!flag[i]){
flag[i]=true;
path[u]=i;
dfs(u+1);
flag[i]=false;
}
}
}
}
六、皇后问题
皇后问题
给出一个n×n的国际象棋棋盘,你需要在棋盘中摆放n个皇后,使得任意两个皇后之间不能互相攻击。具体来说,不能存在两个皇后位于同一行、同一列,或者同一对角线。请问共有多少种摆放方式满足条件。
输入描述:
一行,一个整数n(1≤n≤12),表示棋盘的大小。
输出描述:
输出一行一个整数,表示总共有多少种摆放皇后的方案,使得它们两两不能互相攻击。
示例1
输入
4
输出
2
import java.util.*;
public class Main{
static int n,N=10010,ans;
static char map[][]=new char[N][N];
static boolean[] col=new boolean[N],ug=new boolean[N],udg=new boolean[N];
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
for(int i=0;i<n;i++) for(int j=0;j<n;j++)map[i][j]='.';
dfs(0);
System.out.println(ans);
}
static void dfs(int x){
if(x==n){
ans++;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
//System.out.print(map[i][j]+" ");
}
//System.out.println();
}
//System.out.println();
return;
}
for(int i=0;i<n;i++){
if(!col[i]&&!ug[x+i]&&!udg[n-x+i]){
map[x][i]='Q';
col[i]=ug[x+i]=udg[n-x+i]=true;
dfs(x+1);
map[x][i]='.';
col[i]=ug[x+i]=udg[n-x+i]=false;
}
}
}
}
七、 池塘
Lake Counting
由于最近的降雨,Farmer John 的田地里多处积水,用 N x M (1 <= N <= 100;1 <= M <= 100) 的矩形表示。每个方格包含水 ('W') 或旱地 ('.')。农民 John 想弄清楚他的田里有多少个池塘。池塘是一组相连的方块,其中有水,其中一个方块被认为与其所有八个相邻方块相邻。
给定 Farmer John 的田地图,确定他有多少个池塘。
输入描述:
第 1 行:两个以空格分隔的整数:N 和 M * 第 2..N+1 行:每行 M 个字符,表示 Farmer John 字段的一行。每个字符都是 'W' 或 '.'。字符之间没有空格。
输出描述:
第1 行:Farmer John 田地中的池塘数量。
示例1
输入
10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.
输出
3
import java.util.Arrays;
import java.util.Scanner;
public class Main{
static int N=100,n,m;
static char[][] d=new char[N][N];
static int dx[]=new int[]{1,0,0,-1,1,-1,1,-1};
static int dy[]=new int[]{0,1,-1,0,1,-1,-1,1};
static boolean flag[][]=new boolean[N][N];
static int ans=0;
public static void main( String[]args ){
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
for(int i=0;i<n;i++) d[i]=sc.next().toCharArray();
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(!flag[i][j]&&d[i][j]=='W'){
dfs(i,j);
ans++;
}
}
}
System.out.println(ans);
}
static void dfs(int x,int y){
if(d[x][y]=='.')return;
for(int i=0;i<8;i++){
int xx=x+dx[i];
int yy=y+dy[i];
if(xx>=0&&yy>=0&&xx<n&&yy<m&&d[xx][yy]=='W'&&!flag[xx][yy]){
flag[xx][yy]=true;
dfs(xx,yy);
}
}
}
}
八、迷宫
迷宫
这是一个关于二维迷宫的题目。我们要从迷宫的起点 'S' 走到终点 'E',每一步我们只能选择上下左右四个方向中的一个前进一格。 'W' 代表墙壁,是不能进入的位置,除了墙壁以外的地方都可以走。迷宫内的 'D' 代表一道上锁的门,只有在持有钥匙的时候才能进入。而 'K' 则代表了钥匙,只要进入这一格,就会自动地拿到钥匙。最后 '.' 则是代表空无一物的地方,欢迎自在的游荡。
本题的迷宫中,起点、终点、门跟钥匙这四个特殊物件,每一个恰好会出现一次。而且,此迷宫的四周 (最上面的一行、最下面的一行、最左边的一列以及最右边的一列) 都会是墙壁。
请问,从起点到终点,最少要走几步呢?
输入描述:
输入的第一行有两个正整数H, W,分别代表迷宫的长跟宽。
接下来的H行代表迷宫,每行有一个长度恰为W的字串,此字串只包含`'S'`, `'E'`, `'W'`, `'D '`, `'K'`, `'.'`这几种字元。
输出描述:
请在一行中输出一个整数代表答案,如果无法从起点走到终点,请输出-1。
示例1
输入
4 12
WWWWWWWWWWWW
WE.W.S..W.KW
W..D..W....W
WWWWWWWWWWWW
输出
20
示例2
输入
6 6
WWWWWW
WEWS.W
W.WK.W
W.WD.W
W.W..W
WWWWWW
输出
-1
备注:
4 ≤ H, W≤ 500
'S', 'E', 'K', 'D'各出现恰好一次
迷宫的四周(最上面的一行、最下面的一行、最左边的一列以及最右边的一列) 都会是 'W'
import java.util.*;
import java.io.*;
class Node {
int x, y;
int len;
boolean hasKey = false;
public Node(int x, int y, int len, boolean hasKey) {
this.x = x;
this.y = y;
this.len = len;
this.hasKey = hasKey;
}
}
public class Main {
static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static final Queue<Node> q = new ArrayDeque<>();
static final int[][] dir = { { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 } };
static int n;
static int m;
static char[][] mapp;
static int bfs() {
while (!q.isEmpty()) {
Node cur = q.poll();
if (mapp[cur.x][cur.y] == 'E')
return cur.len;
else if (mapp[cur.x][cur.y] == 'K')
cur.hasKey = true;
if (!cur.hasKey)
mapp[cur.x][cur.y] = 'D';
else
mapp[cur.x][cur.y] = 'W';
for (int[] d : dir) {
int tx = cur.x + d[0];
int ty = cur.y + d[1];
if (tx < 0 || tx >= n || ty < 0 || ty >= m || mapp[tx][ty] == 'W')
continue;
if (!cur.hasKey && mapp[tx][ty] == 'D')
continue;
q.add(new Node(tx, ty, cur.len + 1, cur.hasKey));
}
}
return -1;
}
public static void main(String[] args) throws IOException {
String[] ins = br.readLine().split(" ");
n = Integer.parseInt(ins[0]);
m = Integer.parseInt(ins[1]);
mapp = new char[n][m];
for (int i = 0; i < n; i++) {
mapp[i] = br.readLine().toCharArray();
}
Node nd = null;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (mapp[i][j] == 'S') {
nd = new Node(i, j, 0, false);
q.add(nd);
break;
}
}
}
int res = bfs();
System.out.println(res);
br.close();
}
}
九、八数码问题
在一个 3×3 的网格中,1∼8 这 8个数字和一个 x
恰好不重不漏地分布在这 3×3 的网格中。
例如:
1 2 3
x 4 6
7 5 8
在游戏过程中,可以把 x
与其上、下、左、右四个方向之一的数字交换(如果存在)。
我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
1 2 3
4 5 6
7 8 x
例如,示例中图形就可以通过让 x
先后与右、下、右三个方向的数字交换成功得到正确排列。
交换过程如下:
1 2 3 1 2 3 1 2 3 1 2 3
x 4 6 4 x 6 4 5 6 4 5 6
7 5 8 7 5 8 7 x 8 7 8 x
现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。
输入格式
输入占一行,将 3×3 的初始网格描绘出来。
例如,如果初始网格如下所示:
1 2 3
x 4 6
7 5 8
则输入为:1 2 3 x 4 6 7 5 8
输出格式
输出占一行,包含一个整数,表示最少交换次数。
如果不存在解决方案,则输出 −1。
输入样例:
2 3 4 1 5 x 7 6 8
输出样例
19
import java.io.*;
import java.util.*;
class Main {
static void swap(char[] a, int i, int j) {
char tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
static int bfs(String start, String end) {
Map<String, Integer> map = new HashMap<>();
Queue<String> q = new LinkedList<>();
q.offer(start);
map.put(start, 0);
int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
while (!q.isEmpty()) {
String s = q.poll();
if (s.equals(end)) return map.get(s);
// 获得x的位置
int k = s.indexOf('x');
int x = k / 3, y = k % 3;
for (int i = 0; i < 4; i++) {
int a = x + dx[i], b = y + dy[i];
if (a < 3 && a >= 0 && b < 3 && b >= 0) {
// 对字符串中的字符进行更改
char[] arr = s.toCharArray();
swap(arr, k, a * 3 + b);
String str = new String(arr);
// 如果没有被遍历过
if (map.get(str) == null) {
map.put(str, map.get(s) + 1);
q.offer(str);
}
}
}
}
return -1;
}
public static void main(String[] args) throws IOException {
BufferedReader cin = new BufferedReader(new InputStreamReader(System.in));
String[] s = cin.readLine().split(" ");
String start = "";
for (int i = 0; i < s.length; i++) start += s[i];
String end = "12345678x";
System.out.println(bfs(start, end));
}
}
十、 字符变换
[NOIP2002]字串变换
已知有两个字串 A, B及一组字串变换的规则(至多6个规则):
A1 -> B1
A2 -> B2
规则的含义为:在A中的子串 A1可以变换为 B1、A2可以变换为 B2 …。
例如:A='abcd' B='xyz'
变换规则为:
‘abc’->‘xu’ ‘ud’->‘y’ ‘y’->‘yz’
则此时,A 可以经过一系列的变换变为 B,其变换的过程为:
‘abcd’->‘xud’->‘xy’->‘xyz’
共进行了三次变换,使得A变换为B。
输入描述:
输入格式如下:
A B
A1 B1 \
A2 B2 |-> 变换规则
... ... /
所有字符串长度的上限为 20。
输出描述:
输出格式如下:
若在10步(包含 10步)以内能将A变换为B,则输出最少的变换步数;否则输出"NO ANSWER!"
示例1
输入
abcd xyz
abc xu
ud y
y yz
输出
3
import java.io.*;
import java.util.*;
public class Main {
static String s1,s2;
static Map<String, List<String>> map = new HashMap<>();
public static void main(String[] args) throws IOException {
Scanner scan = new Scanner(System.in);
s1 = scan.next();
s2 = scan.next();
while (scan.hasNext()){
String key = scan.next();
String val = scan.next();
if(map.containsKey(key)){
map.get(key).add(val);
}else{
List<String> list = new LinkedList<>();
list.add(val);
map.put(key, list);
}
}
int b = bfs();
if(b != -1){
out.println(b);
}else{
out.println("NO ANSWER!");
}
out.flush();
out.close();
}
static int bfs(){
Set<String> set = new HashSet<>();
Deque<String> queue = new LinkedList<>();
queue.add(s1);
set.add(s1);
int ans = 0;
while (queue.size() != 0){
int si = queue.size();
for (int i = 0; i < si; i++) {
String poll = queue.poll();
if(poll.equals(s2)){
return ans;
}
for(String key: map.keySet()){
for(String val: map.get(key)){
for (int j = 0; j <= poll.length() - key.length(); j++) {
if(poll.startsWith(key, j)){
String str = new StringBuilder(poll).replace(j, j + key.length(), val).toString();
if(!set.contains(str)){
set.add(str);
queue.add(str);
}
}
}
}
}
}
ans++;
if(ans > 9) return -1;
}
return -1;
}
static PrintWriter out = new PrintWriter(System.out);
}