【算法练习】杂题混讲三:Acwing + 蓝桥杯练习 + 青云杯题目

本文精选了多个算法竞赛题目并提供了详细的解决方案,包括模拟、贪心、数据结构、完全平方数、二分查找等核心算法的应用,适用于算法初学者及竞赛选手提升技能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1373、两只奶牛

在这里插入图片描述
在这里插入图片描述
模拟牛、农民的移动

import java.util.*;
import java.io.*;

public class Main {
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    static char[][] map = new char[10][10];
    static boolean check(int x, int y) {
    	if (x < 0 || y < 0 || x >= 10 || y >= 10 || map[x][y] == '*') return false;
    	return true;
    }
    public static void main(String[] args) throws IOException {
    	Scanner scanner = new Scanner(System.in);
    	int x1 = 0, x2 = 0;
    	int y1 = 0, y2 = 0;
    	for (int i = 0; i < 10; i++) {
    		map[i] = scanner.nextLine().toCharArray();
    		for (int j = 0; j < 10; j++) {
    			if (map[i][j] == 'F') {
    				x1 = i;
    				y1 = j;
    			}
    			if (map[i][j] == 'C') {
    				x2 = i;
    				y2 = j;
    			}
    		}
    	}
    	// 分别代表:上右下左
    	int[][] dirs = new int[][] {{-1,0},{0,1},{1,0},{0,-1}};
    	// 起始方向
    	int d1 = 0;
    	int d2 = 0;
    	boolean flag = false;
    	for (int i = 0; i < 10000000; i++) {
    		if (x1 == x2 && y1 == y2) {
    			System.out.println(i);
    			flag = true;
    			break;
    		}
            int nx1 = x1 + dirs[d1][0];
            int ny1 = y1 + dirs[d1][1];
            int nx2 = x2 + dirs[d2][0];
            int ny2 = y2 + dirs[d2][1];

            if (!check(nx1, ny1)) {
                d1 = (d1 + 1) % 4;
            } else {
                x1 = nx1;
                y1 = ny1;
            }

            if (!check(nx2, ny2)) {
                d2 = (d2 + 1) % 4;
            } else {
                x2 = nx2;
                y2 = ny2;
            }
    	}
    	if (flag == false) System.out.println(0);
    }
}

唯一要注意的是如何判断永不相遇的情况,Y总的思路是在10×10的矩阵中有100个位置,每个位置有4个状态,所有400个状态,人和牛都有400个状态,所以有400×400 = 1.6e5种状态,所以最多重复这么多次后必然会循环,说明永远无法相遇。

1353、滑雪场设计

在这里插入图片描述
在这里插入图片描述
可以先将山峰高度排序,只要保证最大山峰高度 - 最小山峰的高度 <= 17即可,也即我们要找到一个最优的最小山峰高度,使得最大山峰高度 - 最小山峰高度 <= 17,并且取得这个最小山峰的代价最小。这个最优最小的山峰高度所在范围应该是:[最小山峰高度,最大山峰高度-17]

例如题目中的最佳最小山峰高度就 = 4,所以只需要去枚举最优最小的山峰高度,对于所有山峰高度,如果小于它,就要增加高度,大于它+17,就要减少高度,取最优最小的山峰高度的最小值即可。(确实是贪心的思想,但是自己没想到)

import java.util.*;
import java.io.*;

public class Main {
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    public static void main(String[] args) throws IOException {
    	String[] input = reader.readLine().trim().split(" ");
    	int n = Integer.parseInt(input[0]);
    	int[] hei = new int[n];
    	for (int i = 0; i < n; i++) {
    		hei[i] = Integer.parseInt(reader.readLine().trim());
    	}
    	Arrays.sort(hei);
    	// 遍历范围[最小山峰高度, 最大山峰高度-17]
    	int ans = Integer.MAX_VALUE;
    	for (int i = hei[0]; i <= hei[n - 1] - 17; i++) {
    		int tmp = 0;
    		for (int cur : hei) {
    			if (cur < i) tmp += (cur - i) * (cur - i);
    			else if (cur > i + 17) tmp += (cur - i - 17) * (cur - i - 17);
    		}
    		ans = Math.min(tmp, ans);
    	}
    	System.out.println(ans);
    }
}

1354、等差数列(中等)

在这里插入图片描述

在这里插入图片描述
简单模拟,数据量小,先找到双平方数集合,然后枚举可能的等差数列前两项即可。

import java.util.*;
import java.io.*;

class node {
	// 分别记录首项和公差
	int a, b;
	node (int a, int b) {
		this.a = a;
		this.b = b;
	}
}
public class Main {
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    public static void main(String[] args) throws IOException {
    	String[] input = reader.readLine().trim().split(" ");
    	int n = Integer.parseInt(input[0]);
    	input = reader.readLine().trim().split(" ");
    	int m = Integer.parseInt(input[0]);
    	int top = m * m + m * m;
    	boolean[] vis = new boolean[top + 1];
    	// 枚举双平方数集合
    	for (int i = 0; i <= m; i++) {
    		for (int j = 0; j <= m; j++) {
    			vis[i * i + j * j] = true;
    		}
    	}
    	List<node> list = new ArrayList<>();
    	// 枚举等差数列的前两项
    	for (int i = 0; i <= top; i++) {
    		if (!vis[i]) continue;
    		for (int j = i + 1; j <= top; j++) {
    			if (!vis[j]) continue;
    			// 找到数列的公差d,以及在长度n下的最后一项
    			int d = j - i;
    			int last = i + d * (n - 1);
    			if (last > top) break;
    			boolean flag = true;
    			for (int k = j + d; k <= last; k += d) {
    				if (!vis[k]) {
    					flag = false;
    					break;
    				}
    			}
    			// 是满足的
    			if (flag) {
    				list.add(new node(i, d));
    			}
    		}
    	}
    	if (list.isEmpty()) {
    		writer.write("NONE");
    	} else {
        	Collections.sort(list, new Comparator<node>() {
        		@Override
        		public int compare(node o1, node o2) {
        			if (o1.b != o2.b) return o1.b - o2.b;
        			else return o1.a - o2.a;
        		}
        	});
        	for (node cur : list) {
        		writer.write(cur.a + " " + cur.b + "\n");
        	}
    	}
    	writer.flush();
    }
}

1761、阻挡广告牌(简单)

在这里插入图片描述
在这里插入图片描述
把广告牌的所在矩阵位置标记为1,卡车在的位置和没有广告牌的位置都标记为0,统计1的个数即可。

import java.util.*;
import java.io.*;

public class Main {
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
    public static void main(String[] args) throws IOException {
        String[] input = reader.readLine().trim().split(" ");
        int x1 = Integer.parseInt(input[0]) + 1000;
        int y1 = Integer.parseInt(input[1]) + 1000;
        int x2 = Integer.parseInt(input[2]) + 1000;
        int y2 = Integer.parseInt(input[3]) + 1000;

        input = reader.readLine().trim().split(" ");
        int x3 = Integer.parseInt(input[0]) + 1000;
        int y3 = Integer.parseInt(input[1]) + 1000;
        int x4 = Integer.parseInt(input[2]) + 1000;
        int y4 = Integer.parseInt(input[3]) + 1000;

        input = reader.readLine().trim().split(" ");
        int cx1 = Integer.parseInt(input[0]) + 1000;
        int cy1 = Integer.parseInt(input[1]) + 1000;
        int cx2 = Integer.parseInt(input[2]) + 1000;
        int cy2 = Integer.parseInt(input[3]) + 1000;

        int[][] g = new int[2010][2010];
        for (int i = 0; i <= 2000; i++) {
            for (int j = 0; j <= 2000; j++) {
                if (i > x1 && i <= x2 && j > y1 && j <= y2) {
                    g[i][j] = 1;
                }
                if (i > x3 && i <= x4 && j > y3 && j <= y4) {
                    g[i][j] = 1;
                }
                if (i > cx1 && i <= cx2 && j > cy1 && j <= cy2) {
                    g[i][j] = 0;
                }
            }
        }
        int ans = 0;
        for (int i = 0; i <= 2000; i++) {
            for (int j = 0; j <= 2000; j++) {
                if (g[i][j] == 1) ans++;
            }
        }
        System.out.println(ans);
    }
}

青云杯:完全平方数

在这里插入图片描述
这道题是蓝桥杯2021年原题,很简单,把当前给出的n进行质因数分解,例如12 = 2 x 2 x 3,把奇数个的质因数,补成偶数个,12就还需要补个3,就可以达到完全平方数,也就是说完全平方数的质因子数一定是偶数个。

当是指数时,例如:17,只能分出17,所以再乘上自己即可。

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        long n = scan.nextLong();
        long res = 1;
        // 只需要考虑到根号n即可
        for (long i = 2; i * i <= n; i++) {
            if (n % i == 0) {
                int cnt = 0;
                while (n % i == 0) {
                	// n也是跟着除的
                    n /= i;
                    cnt++;
                }
                // 累乘指数为奇数的质因子
                if (cnt % 2 == 1) {
                    res *= i;
                }
            }
        }
        // n为质数时,res=1,满足
        System.out.println(res * n);
    }
}

青云杯:列车调度

在这里插入图片描述
模拟火车进轨道的过程, 第一辆火车肯定直接进入,新开一个轨道,后序火车应该放在数字比自身大的火车的轨道后面(也就是替换掉当前轨道的之前的火车),也就是二分中的:找到第一个比自身大的数!,如果没找到就会返回数组长度。

比赛场上,根本没想到…,只知道应该把递减的数字放在一起,看可以拆分出多少个递减数组序列,看了看数据规模觉得是二分、排序、双指针,但是没想到真是二分…

import java.util.*;
import java.io.*;

public class Main {
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    static int[] dp = new int[100005];
    static int cnt = 0;
    static int binarySearch(int v) {
    	int left = 0, right = cnt;
    	// 找第一个大于key的下标
    	while (left < right) {
    		int mid = (left + right) >> 1;
    		if (dp[mid] > v) {
    			right = mid;
    		} else {
    			left = mid + 1;
    		}
    	}
    	return left;
    }
    public static void main(String[] args) throws IOException, Exception {
    	String[] input = reader.readLine().trim().split(" ");
    	int n = Integer.parseInt(input[0]);
    	int[] num = new int[n];
    	input = reader.readLine().trim().split(" ");
    	for (int i = 0; i < n; i++) {
    		int cur = Integer.parseInt(input[i]);
    		if (i == 0) dp[cnt++] = cur;  // 第一辆车进来
    		else {
    			// 二分找这辆车应该放在的轨道,应该放在比前面数更小的轨道上
    			// 当前列车小于最后一个进去的列车时,就二分查找到第一个大于它的位置并将其值改为当前列车
    			int next = binarySearch(cur);
    			if (next == cnt) dp[cnt++] = cur;  // 说明没找到大于当前数的下标位置,说明要新开铁轨
    			else dp[next] = cur;
    		}
    	}
    	System.out.println(cnt);
    }
}

青云杯:完全二叉树的层序遍历

在这里插入图片描述
因为题目给出的是完全二叉树,所以才使得通过后序遍历得到层序遍历成为了可能。一定要区分:完美二叉树和完全二叉树,完美二叉树也称满二叉树,由其名字可以知道,它的节点都是塞满了的。而完全二叉树,可能是满二叉树,也有可能不是,它可以最后一层节点不放满,但必须从左到右放。

import java.util.*;
import java.io.*;

public class Main {
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    static int n;
    static int[] tree = new int[32];
    static Scanner scan = new Scanner(System.in);
    public static void main(String[] args) throws IOException, Exception {
    	n = scan.nextInt();
    	create(1);  // 根节点为1
    	// 通过后序遍历构建出来的树,直接输出该数组就是层序遍历结果
    	for (int i = 1; i <= n; i++) {
    		if (i > 1) System.out.print(" ");
    		System.out.print(tree[i]);
    	}
    }
    static void create(int i) throws Exception, IOException {
    	if (i > n) return;
    	// 利用完全二叉树的特性,左子树节点:2 * i,右子树节点:2 * i + 1
    	create(2 * i);  // 根节点的左孩子节点
    	create(2 * i + 1);  // 根节点的有孩子节点
    	// 后序遍历的特点,先构建左、右孩子,再构建根节点
    	tree[i] = scan.nextInt();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@u@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值