Auxiliary Set(dfs求每个节点的儿子个数)

本文介绍了一种基于深度优先搜索的算法,用于解决在一个带有重要顶点的有根树中进行辅助集大小查询的问题。该算法首先通过DFS计算每个顶点的子树大小,并将不重要的顶点按深度排序,然后迭代地确定哪些顶点应当加入辅助集中。

Auxiliary Set

Time Limit: 9000/4500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 491    Accepted Submission(s): 136


Problem Description
Given a rooted tree with n vertices, some of the vertices are important.

An auxiliary set is a set containing vertices satisfying at least one of the two conditions:

It is an important vertex
It is the least common ancestor of two different important vertices.

You are given a tree with n vertices (1 is the root) and q queries.

Each query is a set of nodes which indicates the unimportant vertices in the tree. Answer the size (i.e. number of vertices) of the auxiliary set for each query.
 

Input
The first line contains only one integer T (T1000), which indicates the number of test cases.

For each test case, the first line contains two integers n (1n100000), q (0q100000).

In the following n -1 lines, the i-th line contains two integers ui,vi(1ui,vin) indicating there is an edge between uii and vi in the tree.

In the next q lines, the i-th line first comes with an integer mi(1mi100000) indicating the number of vertices in the query set.Then comes with mi different integers, indicating the nodes in the query set.

It is guaranteed that qi=1mi100000.

It is also guaranteed that the number of test cases in which n1000  or qi=1mi1000 is no more than 10.
 

Output
For each test case, first output one line "Case #x:", where x is the case number (starting from 1).

Then q lines follow, i-th line contains an integer indicating the size of the auxiliary set for each query. 
 

Sample Input
1 6 3 6 4 2 5 5 4 1 5 5 3 3 1 2 3 1 5 3 3 1 4
 

Sample Output
Case #1: 3 6 3
Hint
For the query {1,2, 3}: •node 4, 5, 6 are important nodes For the query {5}: •node 1,2, 3, 4, 6 are important nodes •node 5 is the lea of node 4 and node 3 For the query {3, 1,4}: • node 2, 5, 6 are important nodes

用dfs先求出所有点的儿子树和父亲节点,分别记录在son[]和fa[]数组中,我们要找不重要节点中能加入集合中的点,把不重要的点按深度由大到小排序后遍历,如果该点son[]大于2,那么这个点可以加入集合,如果这个点的son为0 那么son[fa[i]]-1(i表示这个点),这个比较巧妙,可以弄几个样例模拟下。

答案就出来了。

#include<iostream>
#include<string.h>
#include<algorithm>
#include<cstdio>
#include<vector>
using namespace std;
int level[100005];
int son[100005];
vector<int>V[100005];
int a[100005];
int child[100005];
int fa[100005];
void dfs(int root)
{
	son[root]=0;
	for(int i=0;i<V[root].size();i++)
	{
		if(level[V[root][i]]==0)
		{
		  fa[V[root][i]]=root;
		  level[V[root][i]]=level[root]+1;
		  dfs(V[root][i]);
		  son[root]++;
		}
	}
}
bool cmp(int a,int b)
{
	return level[a]>level[b];
}
int main()
{
	int t;
	scanf("%d",&t);
	int nn=0;
	while(t--)
	{
		printf("Case #%d:\n",++nn);
		memset(level,0,sizeof(level));
		level[1]=1;
		int n,q;
		scanf("%d%d",&n,&q);
		int u,v;
		for(int i=0;i<=n;i++)
		{
			V[i].clear();
		}
		for(int i=0;i<n-1;i++)
		{
			scanf("%d%d",&u,&v);
			V[u].push_back(v);
			V[v].push_back(u);
		}
		fa[1]=0;
		dfs(1);
		for(int i=0;i<q;i++)
		{
			int num;
			scanf("%d",&num);
			int ans=n-num;
			for(int i=0;i<num;i++)
			{
				scanf("%d",&a[i]);
			}
			sort(a,a+num,cmp);
			for(int i=0;i<num;i++)
			{
				child[a[i]]=son[a[i]];
			}
			for(int i=0;i<num;i++)
			{
				if(child[a[i]]>=2)
				{
					ans++;
				}
				else if(child[a[i]]==0)
				{
					child[fa[a[i]]]--;
				}
			}
			printf("%d\n",ans);
		}
	}
}




import sys from collections import deque import matplotlib.pyplot as plt import networkx as nx import numpy as np plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文显示问题 plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题 class DinicVisual: def __init__(self, n, edges, visualize=True): """ :param n: 节点数 :param edges: 边列表 [(u, v, lb, ub)] :param visualize: 是否可视化 """ self.n = n self.original_edges = edges self.visualize = visualize self.fig, self.ax = plt.subplots(figsize=(14, 10)) self.fig.suptitle("无源汇上下界可行流算法动态可视化", fontsize=16) # 初始化超级源汇 self.s = n self.t = n + 1 self.total_nodes = n + 2 # 计算每个节点的流量差 self.A = [0] * n for u, v, lb, ub in edges: self.A[u] -= lb self.A[v] += lb # 创建Dinic数据结构 self.graph = [[] for _ in range(self.total_nodes)] self.level = [-1] * self.total_nodes self.cur = [0] * self.total_nodes self.edge_info = {} # 存储边信息 # 添加原图中的边 self.original_edge_refs = [] for i, (u, v, lb, ub) in enumerate(edges): cap = ub - lb # 添加边并记录信息 self.add_edge(u, v, cap, (i, lb, ub, f"e{i}")) self.original_edge_refs.append((u, v, len(self.graph[u]) - 1, lb)) # 添加超级源汇的边 self.total_flow = 0 for i in range(n): if self.A[i] > 0: self.add_edge(self.s, i, self.A[i], (f"s→{i}", "source")) self.total_flow += self.A[i] elif self.A[i] < 0: self.add_edge(i, self.t, -self.A[i], (f"{i}→t", "sink")) # 初始化可视化 if self.visualize: self.initialize_visualization() def add_edge(self, u, v, cap, info=None): """添加边并存储信息""" forward = [v, cap, 0, info] # [目标, 容量, 流量, 信息] reverse = [u, 0, 0, None] # 反向边 forward[2] = reverse reverse[2] = forward self.graph[u].append(forward) self.graph[v].append(reverse) # 存储边信息用于可视化 if info: self.edge_info[(u, v)] = { 'capacity': cap, 'flow': 0, 'info': info } return forward def bfs(self): """BFS分层并可视化""" self.level = [-1] * self.total_nodes queue = deque([self.s]) self.level[self.s] = 0 # 可视化:显示BFS搜索过程 if self.visualize: self.visualize_step(f"BFS分层: 访问源点s (L0)") plt.pause(1.0) while queue: u = queue.popleft() for i, edge in enumerate(self.graph[u]): v, cap, rev, info = edge if cap > 0 and self.level[v] == -1: self.level[v] = self.level[u] + 1 queue.append(v) # 可视化:显示新访问的节点 if self.visualize: if v < self.n: node_label = f"节点{v}" else: node_label = "汇点t" if v == self.t else "源点s" self.visualize_step(f"BFS分层: 访问{node_label} (L{self.level[v]})") plt.pause(0.3) return self.level[self.t] != -1 def dfs(self, u, t, flow, path=None): """DFS查找增广路径并可视化""" if path is None: path = [] if u == t: # 可视化:显示找到的增广路径 if self.visualize: path_desc = "→".join([f"{'s' if p == self.s else 't' if p == self.t else p}" for p in path + [t]]) self.visualize_step(f"找到增广路径: {path_desc}\n流量: {flow}") plt.pause(1.5) return flow for i in range(self.cur[u], len(self.graph[u])): self.cur[u] = i edge = self.graph[u][i] v, cap, rev, info = edge if cap > 0 and self.level[v] == self.level[u] + 1: # 可视化:显示当前探索的边 if self.visualize: edge_desc = self.get_edge_description(u, v) self.visualize_step(f"探索: {edge_desc} (剩余容量: {cap})") plt.pause(0.5) f = self.dfs(v, t, min(flow, cap), path + [u]) if f > 0: # 更新边流量 edge[1] -= f rev[1] += f # 更新可视化信息 if (u, v) in self.edge_info: self.edge_info[(u, v)]['flow'] += f elif (v, u) in self.edge_info: # 处理反向边 self.edge_info[(v, u)]['flow'] -= f # 可视化:显示流量更新 if self.visualize: edge_desc = self.get_edge_description(u, v) self.visualize_step(f"更新: {edge_desc}\n增加流量: {f}") plt.pause(0.8) return f return 0 def max_flow(self): """计算最大流并动态可视化""" total_flow = 0 iteration = 1 while self.bfs(): self.cur = [0] * self.total_nodes if self.visualize: self.visualize_step(f"开始阶段 {iteration} (分层完成)") plt.pause(1.0) while True: flow = self.dfs(self.s, self.t, float('inf')) if flow == 0: break total_flow += flow if self.visualize: self.visualize_step(f"阶段 {iteration} 完成\n累计流量: {total_flow}/{self.total_flow}") plt.pause(1.0) iteration += 1 # 检查可行解 if total_flow != self.total_flow: if self.visualize: self.visualize_step(f"无可行解!\n需流量: {self.total_flow}, 实际流量: {total_flow}") plt.pause(3.0) return None # 计算原图中每条边的实际流量 flows = [] for u, v, idx, lb in self.original_edge_refs: edge = self.graph[u][idx] flow_in_additional = (edge[3][2] - edge[3][1]) - edge[1] actual_flow = lb + flow_in_additional flows.append(actual_flow) if self.visualize: self.visualize_final_flow(flows) plt.pause(5.0) return flows def get_edge_description(self, u, v): """获取边的描述信息""" if u == self.s: return f"s → {v}" elif v == self.t: return f"{u} → t" elif (u, v) in self.edge_info: info = self.edge_info[(u, v)]['info'] if isinstance(info, tuple) and len(info) > 3: return f"{u} → {v} ({info[3]})" return f"{u} → {v}" def initialize_visualization(self): """初始化可视化布局""" self.G = nx.DiGraph() # 添加节点 for i in range(self.n): self.G.add_node(i, label=f"{i}") self.G.add_node(self.s, label="s") self.G.add_node(self.t, label="t") # 添加边 for u in range(self.total_nodes): for edge in self.graph[u]: v, cap, _, info = edge if cap > 0: # 只添加正向边 self.G.add_edge(u, v, capacity=cap, flow=0) # 创建环形布局 self.pos = {} # 普通节点布置在圆上 angles = np.linspace(0, 2 * np.pi, self.n, endpoint=False) for i in range(self.n): angle = angles[i] self.pos[i] = (np.cos(angle), np.sin(angle)) # 超级源点和汇点 self.pos[self.s] = (0, 1.5) # 上方 self.pos[self.t] = (0, -1.5) # 下方 # 初始绘图 self.ax.clear() nx.draw_networkx_nodes(self.G, self.pos, node_size=800, node_color=['lightblue' if n < self.n else 'salmon' for n in self.G.nodes()]) nx.draw_networkx_labels(self.G, self.pos, labels={n: d['label'] for n, d in self.G.nodes(data=True)}) # 绘制边 self.edge_collection = nx.draw_networkx_edges( self.G, self.pos, arrowstyle='->', arrowsize=20, edge_color='gray', width=1, ax=self.ax ) # 初始化边标签 self.edge_labels = {} for u, v in self.G.edges(): self.edge_labels[(u, v)] = self.ax.text(0, 0, "", fontsize=8, ha='center', va='center') self.ax.set_title("初始化网络", fontsize=14) self.ax.set_axis_off() plt.tight_layout() plt.pause(2.0) def visualize_step(self, message): """可视化当前步骤""" self.ax.clear() # 绘制节点 nx.draw_networkx_nodes(self.G, self.pos, node_size=800, node_color=['lightblue' if n < self.n else 'salmon' for n in self.G.nodes()]) nx.draw_networkx_labels(self.G, self.pos, labels={n: d['label'] for n, d in self.G.nodes(data=True)}) # 绘制边并设置颜色和宽度 edge_colors = [] edge_widths = [] for u, v in self.G.edges(): # 获取当前边的状态 cap = self.G[u][v]['capacity'] flow = self.edge_info.get((u, v), {}).get('flow', 0) # 计算饱和度 saturation = flow / cap if cap > 0 else 0 # 使用颜色表示饱和度 edge_colors.append(plt.cm.RdYlGn(saturation)) # 使用宽度表示流量 edge_widths.append(1 + 3 * saturation) # 绘制边 nx.draw_networkx_edges( self.G, self.pos, arrowstyle='->', arrowsize=20, edge_color=edge_colors, width=edge_widths, ax=self.ax ) # 更新边标签 for (u, v), text in self.edge_labels.items(): # 获取边信息 cap = self.G[u][v]['capacity'] flow = self.edge_info.get((u, v), {}).get('flow', 0) # 特殊边处理 if u == self.s or v == self.t: label = f"{flow}/{cap}" else: # 获取原始边信息 info = self.edge_info.get((u, v), {}).get('info', None) if info and isinstance(info, tuple): _, lb, ub, name = info actual_flow = lb + flow label = f"{name}: {actual_flow}/{ub}\n[{lb},{ub}]" else: label = f"{flow}/{cap}" # 计算边的中点位置 x = (self.pos[u][0] + self.pos[v][0]) / 2 y = (self.pos[u][1] + self.pos[v][1]) / 2 # 更新文本位置和内容 text.set_position((x, y)) text.set_text(label) self.ax.add_artist(text) # 显示当前信息 self.ax.set_title(message, fontsize=14) self.ax.set_axis_off() plt.tight_layout() plt.draw() def visualize_final_flow(self, flows): """可视化最终可行流分配""" self.ax.clear() # 只绘制原图节点 H = nx.DiGraph() for i in range(self.n): H.add_node(i, label=f"{i}") # 添加原图边 for i, (u, v, lb, ub) in enumerate(self.original_edges): H.add_edge(u, v, flow=flows[i], lb=lb, ub=ub, name=f"e{i}") # 环形布局 angles = np.linspace(0, 2 * np.pi, self.n, endpoint=False) pos = {} for i in range(self.n): angle = angles[i] pos[i] = (np.cos(angle), np.sin(angle)) # 绘制节点 nx.draw_networkx_nodes(H, pos, node_size=800, node_color='lightgreen') nx.draw_networkx_labels(H, pos) # 绘制边 edge_colors = [] edge_widths = [] for u, v in H.edges(): flow = H[u][v]['flow'] ub = H[u][v]['ub'] saturation = flow / ub edge_colors.append(plt.cm.RdYlGn(saturation)) edge_widths.append(1 + 3 * saturation) nx.draw_networkx_edges( H, pos, arrowstyle='->', arrowsize=20, edge_color=edge_colors, width=edge_widths, ax=self.ax ) # 添加边标签 edge_labels = {} for u, v in H.edges(): flow = H[u][v]['flow'] lb = H[u][v]['lb'] ub = H[u][v]['ub'] name = H[u][v]['name'] edge_labels[(u, v)] = f"{name}: {flow}\n[{lb},{ub}]" nx.draw_networkx_edge_labels(H, pos, edge_labels=edge_labels, font_size=8) self.ax.set_title("可行流分配结果", fontsize=14) self.ax.set_axis_off() plt.tight_layout() plt.draw() def circulation_flow_visual(n, edges): """无源汇上下界可行流解与可视化""" # 创建可视化实例 dinic_visual = DinicVisual(n, edges, visualize=True) # 计算可行流 flows = dinic_visual.max_flow() if flows is None: print("无可行流解") return None print("\n各边实际流量分配:") for i, (u, v, lb, ub) in enumerate(edges): print(f"边 {u}→{v} ({lb},{ub}): {flows[i]}") plt.show() # 保持窗口打开 return flows if __name__ == "__main__": # 15节点复杂网络示例 print("=" * 50) print("15节点复杂网络的无源汇上下界可行流计算") # 定义15节点网络的边 n = 15 # 节点数 edges = [ # 核心环状结构 (0-4) - 调整下界使总流入=流出 (0, 1, 2, 5), (1, 2, 2, 6), (2, 3, 2, 6), (3, 4, 2, 5), (4, 0, 2, 5), (0, 3, 1, 4), (2, 0, 1, 4), # 中间层级连接 (5-9) - 对称设计 (1, 5, 1, 4), (5, 6, 1, 4), (6, 7, 1, 4), (7, 8, 1, 4), (8, 9, 1, 4), (9, 2, 1, 4), (3, 7, 1, 4), (4, 8, 1, 4), # 外围节点连接 (10-14) - 轻量级连接 (5, 10, 1, 3), (10, 11, 1, 3), (11, 12, 1, 3), (6, 11, 1, 3), (7, 13, 1, 3), (13, 14, 1, 3), (14, 9, 1, 3), (12, 8, 1, 3), (12, 14, 1, 2) ] # 计算并可视化可行流 flows = circulation_flow_visual(n, edges) 1.运行结果是无可行流解,请修改示例,给出15节点的有解示例 2.探索和访问过程没有明显可视化,尝试给出
最新发布
06-14
<think>嗯,用户想了解如何在Oracle的两个节点上配置Data Guard主从。我需要先回忆一下Data Guard的基本概念和配置步骤。首先,Data Guard是Oracle的高可用性解决方案,通过物理或逻辑备用数据库来实现灾难恢复。主数据库的事务会传输到备用数据库,保持数据同步。 接下来,用户提到的是两个节点,主和从。所以需要明确主库和备库的环境准备,比如操作系统是否相同,Oracle版本是否一致,网络配置是否正确,特别是监听器和TNS连接的配置。另外,归档模式是否启用,因为Data Guard依赖归档日志。 然后,步骤方面,可能需要先配置主库的参数文件,比如设置LOG_ARCHIVE_CONFIG、LOG_ARCHIVE_DEST_1和LOG_ARCHIVE_DEST_2。然后创建备用日志文件,因为备库需要这些来应用redo数据。接着,备份主库,可能是使用RMAN来创建物理备份,并传输到备库服务器。 在备库配置部分,需要修改参数文件,尤其是DB_UNIQUE_NAME,然后创建必要的目录结构,比如adump、dpdump等。之后,使用RMAN恢复备库,并配置监听器和TNSNAMES,确保主备之间的通信。 最后,启动Data Guard Broker来管理配置,验证同步状态,可能还需要测试切换和故障转移的情况。需要注意的常见问题包括网络连通性、参数配置错误、权限问题,以及归档日志是否正常传输。 用户可能需要详细的命令和具体参数示例,比如具体的初始化参数设置,RMAN备份和恢复命令,以及如何通过SQL语句验证同步状态。同时,需要提醒用户在实际操作前做好备份,确保环境一致性,避免配置错误导致的问题。</think>以下是在Oracle数据库中配置Data Guard主从同步的详细步骤: ### 一、环境准备 1. **系统要** - 主备节点使用相同操作系统版本 - Oracle数据库版本一致(推荐19c或以上) - 网络互通(建议专用心跳网络) 2. **必要条件** - 主库必须处于ARCHIVELOG模式 - 启用FORCE LOGGING模式 $$sql ALTER DATABASE FORCE LOGGING; $$ ### 二、主库配置 1. **修改参数文件** ```sql ALTER SYSTEM SET LOG_ARCHIVE_CONFIG='DG_CONFIG=(primary_db, standby_db)'; ALTER SYSTEM SET LOG_ARCHIVE_DEST_1='LOCATION=USE_DB_RECOVERY_FILE_DEST VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=primary_db'; ALTER SYSTEM SET LOG_ARCHIVE_DEST_2='SERVICE=standby_db LGWR ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=standby_db'; ``` 2. **创建备用日志文件** ```sql ALTER DATABASE ADD STANDBY LOGFILE GROUP 4 ('/u01/oradata/standby_redo01.log') SIZE 512M; ALTER DATABASE ADD STANDBY LOGFILE GROUP 5 ('/u01/oradata/standby_redo02.log') SIZE 512M; ALTER DATABASE ADD STANDBY LOGFILE GROUP 6 ('/u01/oradata/standby_redo03.log') SIZE 512M; ``` ### 三、备库配置 1. **创建初始化参数文件** ```sql CREATE PFILE='/tmp/initstandby.ora' FROM SPFILE; ``` 修改以下参数: ```properties *.db_unique_name='standby_db' *.log_archive_config='DG_CONFIG=(primary_db, standby_db)' *.fal_server='primary_db' ``` 2. **创建目录结构** ```bash mkdir -p /u01/oradata/standby/{adump,dpdump,pfile,wallet} ``` ### 四、数据同步配置 1. **主库执行RMAN备份** ```bash rman target / RUN { ALLOCATE CHANNEL ch1 TYPE DISK; BACKUP AS BACKUPSET DATABASE PLUS ARCHIVELOG; BACKUP CURRENT CONTROLFILE FOR STANDBY; } ``` 2. **传输文件到备库** ```bash scp /backup/* oracle@standby:/u01/backup/ ``` 3. **备库恢复数据库** ```bash rman auxiliary / DUPLICATE TARGET DATABASE FOR STANDBY FROM ACTIVE DATABASE; ``` ### 五、验证配置 1. **主库检查传输状态** ```sql SELECT DEST_NAME, STATUS, ERROR FROM V$ARCHIVE_DEST WHERE DEST_ID=2; ``` 2. **备库查询应用进度** ```sql SELECT PROCESS, STATUS, THREAD#, SEQUENCE# FROM V$MANAGED_STANDBY; ``` ### 六、启用Data Guard Broker 1. **主备库执行** ```sql ALTER SYSTEM SET DG_BROKER_START=TRUE; ``` 2. **创建Broker配置** ```bash dgmgrl sys/oracle@primary CREATE CONFIGURATION dg_config AS PRIMARY DATABASE IS primary_db CONNECT IDENTIFIER IS primary_db; ADD DATABASE standby_db AS CONNECT IDENTIFIER IS standby_db; ENABLE CONFIGURATION; ``` ### 七、切换测试 1. **主备切换** ```bash dgmgrl sys/oracle@primary SWITCHOVER TO standby_db; ``` ### 常见问题排查 1. **网络连接问题** ```bash tnsping standby_db ``` 2. **日志应用延迟** ```sql SELECT ARCHIVED_THREAD#, ARCHIVED_SEQ#, APPLIED_THREAD#, APPLIED_SEQ# FROM V$ARCHIVE_DEST_STATUS; ``` 3. **同步状态监控** ```sql SELECT PROTECTION_MODE, PROTECTION_LEVEL FROM V$DATABASE; ``` **注意事项:** 1. 确保主备库字符集一致 2. 定期验证数据完整性 3. 监控alert日志中的错误信息 4. 建议使用ASM存储管理 实际部署时,建议先通过Oracle官方提供的Data Guard Broker工具简化配置流程,具体参数需根据实际业务需和硬件配置进行调整。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值