题目## 题目## 题目
解题思路
- 闭图的定义:对于任意两个不同的点 u , v u, v u,v,如果它们的度数之和 d ( u ) + d ( v ) ≥ n d(u) + d(v) \geq n d(u)+d(v)≥n,那么 u u u 和 v v v 必须相邻
- 解题步骤:
- 维护每个点的度数
- 对点按度数从大到小排序
- 检查每对点,如果它们的度数之和≥n且未相连,则需要添加边
- 添加边后更新相应点的度数
- 需要进行两次遍历,因为添加边会改变点的度数,可能产生新的需要连接的点对
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Node {
int id, degree;
Node(int i = 0, int d = 0) : id(i), degree(d) {}
bool operator<(const Node& other) const {
return degree > other.degree;
}
};
int main() {
int n, m;
cin >> n >> m;
// 初始化邻接矩阵和节点数组
vector<vector<int>> graph(n + 1, vector<int>(n + 1, 0));
vector<Node> nodes(n + 1);
for(int i = 1; i <= n; i++) {
nodes[i].id = i;
}
// 读入边并更新度数
for(int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
graph[u][v] = graph[v][u] = 1;
nodes[u].degree++;
nodes[v].degree++;
}
int ans = 0;
// 需要两次遍历
for(int k = 0; k < 2; k++) {
for(int i = 1; i <= n; i++) {
sort(nodes.begin() + i, nodes.end());
for(int j = i + 1; j <= n; j++) {
auto& u = nodes[i];
auto& v = nodes[j];
if(u.degree + v.degree >= n && !graph[u.id][v.id]) {
graph[u.id][v.id] = graph[v.id][u.id] = 1;
u.degree++;
v.degree++;
ans++;
}
}
}
}
cout << ans << endl;
return 0;
}
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = null;
while ((str = br.readLine()) != null) {
if (str.equals("")) continue;
String[] params = str.split(" ");
int n = Integer.parseInt(params[0]), m = Integer.parseInt(params[1]);
int[][] A = new int[n+1][n+1];
int[] degree = new int[n+1];
for (int i = 0; i < m; i++) {
params = br.readLine().split(" ");
int u = Integer.parseInt(params[0]), v = Integer.parseInt(params[1]);
A[u][v] = A[v][u] = 1;
degree[u]++;
degree[v]++;
}
int ans = 0;
boolean[] seen = new boolean[n+1];
for (int i = 0; i < n; i++) {
int index = 0, max = 0;
// 先找出未被遍历过的节点中度最大的
for (int j = 1; j <= n; j++) {
if (seen[j] || degree[j] <= max) continue;
max = degree[j];
index = j;
}
if (index == 0) break;
seen[index] = true;
// 找到与index不相连的,若二者的度之和大于等于n,则进行连线
for (int j = 1; j <= n; j++) {
if (A[index][j] == 1 || index == j || degree[index] + degree[j] < n) continue;
A[index][j] = A[j][index] = 1;
degree[index]++;
degree[j]++;
ans++;
}
}
System.out.println(ans);
}
br.close();
}
}
class Node:
def __init__(self, id=0, degree=0):
self.id = id
self.degree = degree
def __lt__(self, other):
return self.degree > other.degree
n, m = map(int, input().split())
# 初始化邻接矩阵和节点数组
graph = [[0] * (n + 1) for _ in range(n + 1)]
nodes = [Node(i) for i in range(n + 1)]
# 读入边并更新度数
for _ in range(m):
u, v = map(int, input().split())
graph[u][v] = graph[v][u] = 1
nodes[u].degree += 1
nodes[v].degree += 1
ans = 0
# 需要两次遍历
for _ in range(2):
for i in range(1, n + 1):
nodes[i:] = sorted(nodes[i:])
for j in range(i + 1, n + 1):
u, v = nodes[i], nodes[j]
if u.degree + v.degree >= n and not graph[u.id][v.id]:
graph[u.id][v.id] = graph[v.id][u.id] = 1
u.degree += 1
v.degree += 1
ans += 1
print(ans)
算法及复杂度
- 算法:贪心算法
- 时间复杂度: O ( n 2 log n ) \mathcal{O}(n^2 \log n) O(n2logn) - 需要两次遍历所有点对,每次都要排序
- 空间复杂度: O ( n 2 ) \mathcal{O}(n^2) O(n2) - 需要邻接矩阵存储图
解题思路
这是一个无向图遍历问题:
- 从 1 1 1 号节点出发,需要遍历所有节点
- 每条边长度为 1 1 1
- 需要找到最短的遍历路径
关键发现:
- 对于叶子节点,必须走两次(来回)
- 对于非叶子节点,只需要走一次就能到达其他节点
- 最小路程 = 2 2 2 * (边数) - (最远叶子节点的深度)
代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int maxDepth = 0; // 记录最远叶子节点的深度
void dfs(vector<vector<int>>& graph, vector<bool>& visited, int curr, int depth) {
visited[curr] = true;
bool isLeaf = true;
for(int next : graph[curr]) {
if(!visited[next]) {
isLeaf = false;
dfs(graph, visited, next, depth + 1);
}
}
if(isLeaf) {
maxDepth = max(maxDepth, depth);
}
}
int main() {
int n;
cin >> n;
// 构建邻接表
vector<vector<int>> graph(n + 1);
for(int i = 0; i < n - 1; i++) {
int x, y;
cin >> x >> y;
graph[x].push_back(y);
graph[y].push_back(x);
}
// DFS找最远叶子节点
vector<bool> visited(n + 1, false);
dfs(graph, visited, 1, 0);
// 计算结果
cout << 2 * (n - 1) - maxDepth << endl;
return 0;
}
import java.util.*;
public class Main {
static int maxDepth = 0; // 记录最远叶子节点的深度
static void dfs(List<List<Integer>> graph, boolean[] visited, int curr, int depth) {
visited[curr] = true;
boolean isLeaf = true;
for(int next : graph.get(curr)) {
if(!visited[next]) {
isLeaf = false;
dfs(graph, visited, next, depth + 1);
}
}
if(isLeaf) {
maxDepth = Math.max(maxDepth, depth);
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
// 构建邻接表
List<List<Integer>> graph = new ArrayList<>();
for(int i = 0; i <= n; i++) {
graph.add(new ArrayList<>());
}
for(int i = 0; i < n - 1; i++) {
int x = sc.nextInt();
int y = sc.nextInt();
graph.get(x).add(y);
graph.get(y).add(x);
}
// DFS找最远叶子节点
boolean[] visited = new boolean[n + 1];
dfs(graph, visited, 1, 0);
// 计算结果
System.out.println(2 * (n - 1) - maxDepth);
}
}
from collections import deque
def bfs(graph, start, n):
visited = [False] * (n + 1)
depth = [0] * (n + 1)
queue = deque([(start, 0)]) # (节点, 深度)
visited[start] = True
max_depth = 0
while queue:
curr, d = queue.popleft()
max_depth = max(max_depth, d)
for next_node in graph[curr]:
if not visited[next_node]:
visited[next_node] = True
depth[next_node] = d + 1
queue.append((next_node, d + 1))
return max_depth
def solve():
n = int(input())
# 构建邻接表
graph = [[] for _ in range(n + 1)]
for _ in range(n - 1):
x, y = map(int, input().split())
graph[x].append(y)
graph[y].append(x)
# BFS找最远节点
max_depth = bfs(graph, 1, n)
# 计算结果
print(2 * (n - 1) - max_depth)
if __name__ == "__main__":
solve()
算法及复杂度
- 算法:DFS
- 时间复杂度: O ( n ) \mathcal{O}(n) O(n),其中 n n n是节点数
- 空间复杂度: O ( n ) \mathcal{O}(n) O(n),用于存储图和访问数组
回文素数
题目难度:中等
知识点:数学逻辑,数组
解题思路:首先判断数字是否为回文,然后判断数字是否为素数,若都是,则为回文素数。下面具体介绍回文和素数的判断方法。
方法一
回文的判断方法:对数字取余得到个位数字,然后对该数字除以十后取余,得到十位上的数字,随后继续除以十后取余获得百位上的数字,直至数字为0时停止。将各个位上的倒着排列组成新的数字,判断新数字是否与原来的数字相等,若相等,则为回文。
素数的判断方法:若待判断数字为n,由2至n-1共进行n-2次循环,判断该数字是否能被n整除,若能则不是素数,若都不能,则是素数。
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
String[] s=bf.readLine().split(" ");
int l=Integer.parseInt(s[0]);
int r=Integer.parseInt(s[1]);
int count=0;
//循环判断[L, R]区间内的每一个数字是否为即为回文又为素数
for(int i=l;i<=r;i++) {
if(isPalindrome(i)&&isprime(i))
count++;
else
continue;
}
System.out.println(count);
}
//判断一个数是否为素数
public static boolean isprime(int n) {
if(n==1) {
return false;
}
for(int i=2;i<n;i++){
if(n%i==0) {
return false;
}
}
return true;
}
//判断一个数是否为回文
public static boolean isPalindrome(int n) {
int k=n;int num=0;
while(k!=0) {
num=num*10+k%10;
k=k/10;
}
return num==n;
}
}
方法二
回文的判断方法:将数字转换为字符串,用两个下标start和end分别标记字符串的第一个字符和最后一个字符,使字符串中字符分别从由前往后和从由后往前访问,若前后访问过程中字符均相等,则为回文,否则不是,循环结束条件为start<end。
素数的判断方法:如果一个数不是素数,那它就可以写成是两个数字的乘积,而其中较小的一个数字必然小于等于该数的平方根,因此,在循环判断中,只判断2到该数的平方根之间的数能否被整除即可。
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
String[] s=bf.readLine().split(" ");
int l=Integer.parseInt(s[0]);
int r=Integer.parseInt(s[1]);
int count=0;
//循环判断[L, R]区间内的每一个数字是否为即为回文又为素数
for(int i=l;i<=r;i++) {
if(isPalindrome(i)&&isprime(i))
count++;
else
continue;
}
System.out.println(count);
}
//判断一个数是否为素数
public static boolean isprime(int n) {
if(n==1) {
return false;
}
if (n==2){
return true;
}
for(int i=2;i<Math.sqrt(n)+1;i++){
if(n%i==0) {
return false;
}
}
return true;
}
//判断一个数是否为回文
public static boolean isPalindrome(int n){
String s=String.valueOf(n);
int start=0,end=s.length()-1;
while(start<end){
if(s.charAt(start)!=s.charAt(end)){
return false;
}
start++;
end--;
}
return true;
}
}
5664

被折叠的 条评论
为什么被折叠?



