PTA 分而治之 Java

本文介绍了一个基于分而治之策略的算法实现,通过判断城市间的通路和攻击计划,来评估军事战略的可行性。使用Java编程语言,详细解析了如何通过遍历城市和通路来确定攻击计划是否能将敌方城市孤立。

PTA 分而治之 Java

分而治之,各个击破是兵家常用的策略之一。在战争中,我们希望首先攻下敌方的部分城市,使其剩余的城市变成孤立无援,然后再分头各个击破。为此参谋部提供了若干打击方案。本题就请你编写程序,判断每个方案的可行性。

输入格式:
输入在第一行给出两个正整数 N 和 M(均不超过10 000),分别为敌方城市个数(于是默认城市从 1 到 N 编号)和连接两城市的通路条数。随后 M 行,每行给出一条通路所连接的两个城市的编号,其间以一个空格分隔。在城市信息之后给出参谋部的系列方案,即一个正整数 K (≤ 100)和随后的 K 行方案,每行按以下格式给出:

Np v[1] v[2] … v[Np]

其中 Np 是该方案中计划攻下的城市数量,后面的系列 v[i] 是计划攻下的城市编号。

输出格式:
对每一套方案,如果可行就输出YES,否则输出NO

输入样例:
10 11
8 7
6 8
4 5
8 4
8 1
1 2
1 4
9 8
9 1
1 10
2 4
5
4 10 3 8 4
6 6 1 7 5 4 9
3 1 8 4
2 2 8
7 9 8 7 6 5 4 2
输出样例:
NO
YES
YES
NO
NO

乍一看,并查集,判断连通数是否等于剩余城市数
仔细一想,所有的路都走不通了不就是所有城市都被孤立了吗…
可惜最后一个测试用例超时…

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.LinkedList;
import java.util.StringTokenizer;

public class Main {
	public static void main(String[] args) throws IOException {
		Reader.init(System.in);
		int N = Reader.nextInt();
		int M = Reader.nextInt();
		// 读取数据
		linked_list[] path = new linked_list[N + 1]; // 记录有路通向外面的城市
		for (int i = 1; i < path.length; i++) {
			path[i] = new linked_list();
		}
		for (int i = 0; i < M; i++) {
			int s = Reader.nextInt();
			int e = Reader.nextInt();
			path[s].list.add(e); // 只需要记录一个方向即可
			path[s].exits = path[e].exits = 1; // 标记是有路的城市
		}
		int K = Reader.nextInt();
		for (int i = 0; i < K; i++) {
			// 读取攻打计划
			int Np = Reader.nextInt();
			int[] plan = new int[N + 1]; // 记录要攻打的城市
			for (int j = 0; j < Np; j++) {
				plan[Reader.nextInt()] = 1;
			}
			
			boolean isOK = true; // 判断是否可行
			
			for (int j = 1; j < path.length && isOK; j++) {
				// 如果该城市不是本来就孤立,且不在攻打的计划上
				if (path[j].exits == 1 && plan[j] != 1) {
					// 那么查找是否有城市和该城市相连接
					for (int item : path[j].list) {
						// 如果找到了一个不在攻打计划上的城市和该城市相连,则表示计划不可行
						if (plan[item] != 1) {
							isOK = false;
							break;
						}
					}
				}
			}
			if (isOK) {
				System.out.println("YES");
			} else {
				System.out.println("NO");
			}
		}
	}

	static class linked_list {
		int exits = 0;
		LinkedList<Integer> list = new LinkedList<Integer>();
	}
}

// Class for buffered reading int and double values *//*
class Reader {
	static BufferedReader reader;
	static StringTokenizer tokenizer;

	// ** call this method to initialize reader for InputStream *//*
	static void init(InputStream input) {
		reader = new BufferedReader(new InputStreamReader(input));
		tokenizer = new StringTokenizer("");
	}

	static void init(File file) {
		try {
			reader = new BufferedReader(new FileReader(file));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		tokenizer = new StringTokenizer("");
	}

	// ** get next word *//*
	static String next() throws IOException {
		while (!tokenizer.hasMoreTokens()) {
			// TODO add check for eof if necessary
			tokenizer = new StringTokenizer(reader.readLine());
		}
		return tokenizer.nextToken();
	}

	static String nextLine() throws IOException {
		return reader.readLine();
	}

	static int nextInt() throws IOException {
		return Integer.parseInt(next());
	}

	static char nextChar() throws IOException {
		return next().toCharArray()[0];
	}

	static float nextFloat() throws IOException {
		return Float.parseFloat(next());
	}

	static Double nextDouble() throws IOException {
		return Double.parseDouble(next());
	}
}

### Python 中分而治之(Divide and Conquer)算法的实现与应用 #### 分而治之算法简介 分而治之是一种经典的算法设计策略,其核心思想是将复杂问题分解成若干个小规模子问题来解决。通过递归的方式逐步缩小问题规模,最终得到原问题的解决方案。 在 PTA 平台上的题目中,分而治之算法常用于优化时间复杂度较高的场景。例如,在最大子序和问题中,可以通过分而治之方法高效解决问题[^1]。 --- #### 最大子序和问题中的分而治之实现 以下是基于分而治之思想的最大子序和问题的实现: ```python def max_subarray_divide_and_conquer(arr, low, high): if low == high: # 基本情况:只有一个元素 return arr[low] mid = (low + high) // 2 # 将数组分为两部分 # 计算左侧子数组的最大子序和 left_max_sum = max_subarray_divide_and_conquer(arr, low, mid) # 计算右侧子数组的最大子序和 right_max_sum = max_subarray_divide_and_conquer(arr, mid + 1, high) # 跨越中间位置的最大子序和 cross_max_sum = find_crossing_max_sum(arr, low, mid, high) # 返回三个值中的最大者 return max(left_max_sum, right_max_sum, cross_max_sum) def find_crossing_max_sum(arr, low, mid, high): # 找到跨越中间位置的最大子序列和 left_sum = float('-inf') current_sum = 0 for i in range(mid, low - 1, -1): # 向左扩展 current_sum += arr[i] if current_sum > left_sum: left_sum = current_sum right_sum = float('-inf') current_sum = 0 for j in range(mid + 1, high + 1): # 向右扩展 current_sum += arr[j] if current_sum > right_sum: right_sum = current_sum return left_sum + right_sum if __name__ == "__main__": array = [-2, 1, -3, 4, -1, 2, 1, -5, 4] result = max_subarray_divide_and_conquer(array, 0, len(array) - 1) print(result) ``` 上述代码实现了分而治之的核心逻辑,即分别计算左右两侧以及跨越中间区域的最大子序和,并返回三者的最大值。 --- #### 折半查找中的分而治之思想 除了最大子序和问题外,折半查找也是分而治之的经典应用场景之一。它通过对有序列表不断二分操作快速定位目标值的位置[^2]。 以下是折半查找的一个简单实现: ```python def binary_search(arr, target): low, high = 0, len(arr) - 1 while low <= high: mid = (low + high) // 2 if arr[mid] == target: return mid # 找到目标值 elif arr[mid] < target: low = mid + 1 # 在右半边继续查找 else: high = mid - 1 # 在左半边继续查找 return -1 # 如果未找到则返回-1 if __name__ == "__main__": sorted_array = [1, 3, 5, 7, 9, 11, 13, 15] target_value = 7 index = binary_search(sorted_array, target_value) print(index) ``` 此代码展示了如何利用分而治之减少不必要的比较次数,从而提高搜索效率。 --- #### 循环日程安排问题中的分而治之 另一个典型的分而治之问题是循环日程安排问题。该问题的目标是在二维矩阵中合理分配比赛场次,使得每一场比赛都满足特定约束条件[^3]。 下面是具体实现: ```python import numpy as np def generate_schedule(k): n = 2 ** k a = np.zeros((n, n), dtype=int) # 初始化基础矩阵 a[0][0], a[1][1] = 1, 1 a[1][0], a[0][1] = 2, 2 # 使用分而治之填充整个矩阵 for step in range(1, k): half = 2 ** step for row in range(half): for col in range(half): a[row + half][col] = a[row][col] + half # 左下角 a[row][col + half] = a[row + half][col] # 右上角 a[row + half][col + half] = a[row][col] # 右下角 return a if __name__ == "__main__": schedule_matrix = generate_schedule(3) print(schedule_matrix) ``` 这段代码通过递归方式构建了一个完整的赛程表,体现了分而治之的强大能力。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值