历史 history

本文介绍了一个基于并查集的历史交通网络模拟问题。通过维护不同时间点的城市连接状态,解决国王对于道路建设和旅行满意度的问题。涉及并查集的时间限制判断及按秩合并优化。

题目描述
历史学家小A正在研究一个奇怪的王国的历史。当前阶段的任务是研究该国的交通。
根据这个奇怪的王国的史书记载,史书开始记载前这个王国有 n 个城市(城市从 0 开
始标号) ,但所有城市之间都没有道路相连。
每一年,在位的国王会修建一条 x 到 y 的双向道路,一条道路可能被修建多次,但不会
修建起点和终点为同一个城市的道路。
而在这之间,国王会计划进行若干次旅行。对于计划进行的一次旅行 st->ed,如果当
时能完成这次旅行,而 t 年前不能完成这次旅行,那么国王会对之前的建设成果感到满意,
否则他会很生气,并在下一次计划旅行前都让史官记录下错误的修建道路的信息,即把 x、
y 记作(x+n-c) mod n,(y+n-c) mod n。
当然在这些年中也发生了若干次国王的交替,初始国王的 c 值为 0,而每个国王的 c 值
不一定相同,但在国王在位期间 c 值不会改变,新上位的国王开始处于不生气的状态。
请根据史书帮助小 A 得出国王每次对于计划旅行是否满意,从而辅助小 A 能够研究该
国的交通信息。

输入格式
第一行为两个整数 n,m,表示初始城市数和历史书记载的内容数。
接下来 m 行,每行是以下三种格式之一:
1 . K v :表示国王交替,新国王的 c 值为 v
2 . R x y:表示史书上记载的是国王修建了 x 到 y 的双向道路,但注意这个记录的可
能不是实际状况。
3 . T st ed t: 表示国王计划进行的一次 st->ed 的旅行, 且比较的是 t 年前的情况 (国
王可能会和史书开始记载以前的情况比较) ,注意这个记录的肯定是实际情况。
注意只有遇到 R 操作才会使年份的计数+1。

输出格式
输对于每个 T 的记录输出一行, 如果此次计划旅行令国王满意, 则输出 Y, 否则输出 X。

样例输入
3 7
R 0 1
T 0 1 1
K 1
R 0 1
T 0 1 1
R 0 1
T 0 2 1

样例输出
Y
N
Y

数据范围与约定
对于 30%的数据,保证 n<=1000 ,m<=3000。
另 30%的数据满足没有发生国王的交替。
对于 100%的数据,保证 n,m<=300000,0<=v,x,y,st,ed < n,0<=t< m。
数据有梯度

思路

用并查集判断两个点是否连通,但要加一个时间限制,意味是在某事与其父节点联通的。
为了避免超时,我们需要按秩合并(也就是按层数),将规模小的并查集合并到规模大的并查集上。
此题需要注意一个问题,城市的序号是从0开始的,在样例中可以看出来。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <map>
#include <set>
#include <deque>
#include <vector>
#include <queue>
#define N 300009

int re[N];
int tim;
int size[N],ime[N];
int n,m;
int x,y,z;
char s;
int c;
bool angry;


inline void write(int x){
    if(x==0){putchar(48);return;}
    int len=0,dg[20];
    while(x>0){dg[++len]=x%10;x/=10;}
    for(int i=len;i>=1;i--)putchar(dg[i]+48);
}

inline void read(int &x){
    x=0;char c=getchar();
    while(c<'0' || c>'9')c=getchar();
    while(c>='0' && c<='9'){
        x=x*10+c-'0';
        c=getchar();
    } 
}

inline int find(int a,int t)
 {
    while(re[a]!=a&&ime[a]<=t)a=re[a];
    return a;
 }
using namespace std;
int main()
 {
    read(n);
    read(m);
    for(int i=1;i<n;i++)
     re[i]=i,size[i]=1;
    for(int i=1;i<=m;i++)
     {
        scanf("%s",&s);
        switch(s)
         {
            case'K':read(c);
                     angry=0;
                     continue;
            case'R':read(x);
                    read(y);
                    if(angry)
                     {
                        x+=(c%n);x%=n;
                        y+=(c%n);y%=n;
                              }       
                     tim++;
             x=find(x,tim);
             y=find(y,tim);
             if(x==y)continue;
             if(size[x]>size[y])
              {
                size[x]+=size[y];
                re[y]=x;
                ime[y]=tim;
                       }    
                else
                 {
                    size[y]+=size[x];
                    re[x]=y;
                    ime[x]=tim;
                             }     
                continue;
             case'T':read(x);read(y);read(z);
             angry=((find(x,tim-z)==find(y,tim-z))||find(x,tim)!=find(y,tim));
             if(angry)printf("N\n");
              else printf("Y\n");
              continue;                  

           }
      }
 }
<think>我们正在编写一个完整的模块4,该模块用于精简版评估与可视化(仅保留准确率和损失曲线)。我们已经有了一个函数`evaluate_model`,它接受模型、测试数据加载器和训练历史,然后进行评估并绘制图表。现在,我们需要为这个模块提供一个在JupyterNotebook中使用的示例。在JupyterNotebook中,我们通常会加载之前训练好的模型和训练历史,然后使用测试数据加载器来运行评估。因此,我们需要一个示例代码,展示如何加载模型、准备测试数据加载器,并调用`evaluate_model`函数。注意:在模块3中,训练结束后我们已经保存了模型和训练历史一个pth文件。因此,在Jupyter示例中,我们需要先加载这个保存的模型和训练历史。步骤:1.加载之前保存的模型和训练历史。2.准备测试数据加载器(这里假设测试数据加载器已经准备好,或者需要从数据集中创建)。3.调用`evaluate_model`函数进行评估。下面是一个完整的JupyterNotebook代码示例,包括必要的导入和函数定义(但注意,我们只展示模块4的代码,并且假设模块4的代码已经在一个单独的.py文件中定义,或者在Notebook中已经定义了`evaluate_model`函数)。但是,由于我们是在一个Notebook中,我们可以将模块4的代码定义在一个代码单元格中,然后在另一个单元格中运行示例。为了清晰,我们将模块4的代码(包括`plot_training_history`和`evaluate_model`)放在一个单元格,然后下一个单元格是使用示例。但是,根据要求,我们只撰写使用示例的代码,并假设模块4的函数已经定义。因此,我们撰写如下:</think># Jupyter Notebook 使用示例:模型评估与可视化 以下是一个完整的 Jupyter Notebook 使用示例,展示如何加载训练好的模型,执行评估并可视化训练历史: ```python # 模块4:精简版评估与可视化(仅保留准确率和损失曲线) # 在 Jupyter Notebook 中运行此代码 import torch import matplotlib.pyplot as plt import numpy as np from sklearn.metrics import classification_report from tqdm import tqdm import os import ipywidgets as widgets from IPython.display import display, clear_output import json # 设置中文字体支持 plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 def plot_training_history(history, save_path='training_history.png'): """ 绘制训练历史折线图(仅准确率和损失曲线) 参数: history (dict): 包含训练历史的字典 save_path (str): 图表保存路径 """ plt.figure(figsize=(12, 8)) # 1. 损失曲线 plt.subplot(2, 1, 1) plt.plot(history['train_loss'], 'b-', linewidth=2, label='训练损失') plt.plot(history['val_loss'], 'r-', linewidth=2, label='验证损失') plt.title('损失曲线', fontsize=14, fontweight='bold') plt.xlabel('Epoch', fontsize=12) plt.ylabel('损失', fontsize=12) plt.legend(fontsize=12, loc='best') plt.grid(True, linestyle='--', alpha=0.7) # 2. 准确率曲线 plt.subplot(2, 1, 2) plt.plot(history['train_acc'], 'b-', linewidth=2, label='训练准确率') plt.plot(history['val_acc'], 'r-', linewidth=2, label='验证准确率') plt.title('准确率曲线', fontsize=14, fontweight='bold') plt.xlabel('Epoch', fontsize=12) plt.ylabel('准确率', fontsize=12) plt.ylim(0, 1) plt.legend(fontsize=12, loc='best') plt.grid(True, linestyle='--', alpha=0.7) plt.tight_layout() # 确保目录存在 os.makedirs(os.path.dirname(save_path), exist_ok=True) plt.savefig(save_path, dpi=150, bbox_inches='tight') plt.close() # 关闭图表避免在Jupyter中显示两次 print(f"训练历史图表已保存为 {save_path}") # 在Notebook中显示图表 plt.figure(figsize=(12, 8)) plt.subplot(2, 1, 1) plt.plot(history['train_loss'], 'b-', linewidth=2, label='训练损失') plt.plot(history['val_loss'], 'r-', linewidth=2, label='验证损失') plt.title('损失曲线', fontsize=14, fontweight='bold') plt.xlabel('Epoch', fontsize=12) plt.ylabel('损失', fontsize=12) plt.legend(fontsize=12, loc='best') plt.grid(True, linestyle='--', alpha=0.7) plt.subplot(2, 1, 2) plt.plot(history['train_acc'], 'b-', linewidth=2, label='训练准确率') plt.plot(history['val_acc'], 'r-', linewidth=2, label='验证准确率') plt.title('准确率曲线', fontsize=14, fontweight='bold') plt.xlabel('Epoch', fontsize=12) plt.ylabel('准确率', fontsize=12) plt.ylim(0, 1) plt.legend(fontsize=12, loc='best') plt.grid(True, linestyle='--', alpha=0.7) plt.tight_layout() plt.show() return save_path def evaluate_model(model, dataloader, history=None, save_path='training_history.png'): """ 精简版模型评估(仅保留核心指标) 参数: model (torch.nn.Module): 要评估的PyTorch模型 dataloader (DataLoader): 测试数据加载器 history (dict, optional): 训练历史数据 save_path (str, optional): 训练历史图表保存路径 """ print("\n在测试集上评估模型...") model.eval() all_preds = [] all_labels = [] with torch.no_grad(): test_pbar = tqdm(dataloader, desc='测试评估') for inputs, labels in test_pbar: # 确保数据在CPU上 inputs = inputs.cpu() labels = labels.cpu() outputs = model(inputs) _, preds = torch.max(outputs, dim=-1) # 收集结果(忽略填充) mask = labels != -100 # 假设-100表示填充值 valid_preds = preds[mask].cpu().numpy() valid_labels = labels[mask].cpu().numpy() all_preds.extend(valid_preds) all_labels.extend(valid_labels) # 检查类别分布 unique_labels = np.unique(all_labels) print(f"测试集中出现的类别: {unique_labels}") # 动态调整target_names target_names = [] if 0 in unique_labels: target_names.append('非断句') if 1 in unique_labels: target_names.append('断句') # 绘制训练历史(如果有) if history: print("\n绘制训练历史图表...") plot_training_history(history, save_path) # 分类报告 print("\n分类报告:") if len(unique_labels) > 0: report = classification_report( all_labels, all_preds, target_names=target_names, zero_division=0, # 处理除零错误 output_dict=False ) print(report) else: print("无有效标签可用于评估") # 计算整体准确率 accuracy = np.mean(np.array(all_labels) == np.array(all_preds)) print(f"\n总体准确率: {accuracy:.4f}") return accuracy # Jupyter Notebook 交互式模型评估 class ModelEvaluator: def __init__(self): self.model = None self.history = None self.test_loader = None self.setup_widgets() def setup_widgets(self): """设置交互式控件""" # 模型加载控件 self.model_path = widgets.Text( value='poetry_break_model.pth', placeholder='输入模型路径', description='模型路径:', disabled=False ) # 数据加载器控件 self.data_path = widgets.Text( value='test_data_loader.pt', placeholder='输入测试数据加载器路径', description='测试数据:', disabled=False ) # 加载按钮 self.load_button = widgets.Button( description='加载模型和数据', button_style='success', tooltip='点击加载模型和测试数据' ) self.load_button.on_click(self.load_model_and_data) # 评估按钮 self.eval_button = widgets.Button( description='评估模型', button_style='info', tooltip='点击评估模型', disabled=True ) self.eval_button.on_click(self.evaluate) # 结果输出 self.output = widgets.Output() # 布局 box_layout = widgets.Layout( display='flex', flex_flow='column', align_items='stretch', width='80%' ) self.control_panel = widgets.VBox([ self.model_path, self.data_path, widgets.HBox([self.load_button, self.eval_button]), self.output ], layout=box_layout) def load_model_and_data(self, b): """加载模型和测试数据""" with self.output: clear_output() print("正在加载模型和测试数据...") try: # 加载模型 checkpoint = torch.load(self.model_path.value) # 假设模型类定义在另一个模块中 # 这里简化处理,实际使用时需要替换为实际的模型类 self.model = torch.nn.Sequential( torch.nn.Embedding(1000, 128), torch.nn.LSTM(128, 64, batch_first=True), torch.nn.Linear(64, 2) ) self.model.load_state_dict(checkpoint['model_state_dict']) self.model = self.model.cpu() self.model.eval() # 加载历史 self.history = checkpoint['history'] # 加载测试数据加载器 self.test_loader = torch.load(self.data_path.value) print(f"✅ 模型加载成功: {self.model_path.value}") print(f"✅ 测试数据加载成功: {self.data_path.value}") print(f"训练轮次数: {len(self.history['train_loss'])}") print(f"最终训练准确率: {self.history['train_acc'][-1]:.4f}") print(f"最终验证准确率: {self.history['val_acc'][-1]:.4f}") self.eval_button.disabled = False except Exception as e: print(f"❌ 加载失败: {str(e)}") self.eval_button.disabled = True def evaluate(self, b): """执行模型评估""" with self.output: clear_output() print("开始模型评估...") if self.model is None or self.test_loader is None: print("错误: 请先加载模型和测试数据") return try: accuracy = evaluate_model( self.model, self.test_loader, self.history ) # 创建性能摘要 performance = { "模型路径": self.model_path.value, "测试样本数": len(self.test_loader.dataset), "准确率": accuracy, "训练损失": self.history['train_loss'][-1], "验证损失": self.history['val_loss'][-1], "训练准确率": self.history['train_acc'][-1], "验证准确率": self.history['val_acc'][-1] } # 保存性能报告 report_path = "model_performance.json" with open(report_path, 'w') as f: json.dump(performance, f, indent=2) print(f"\n✅ 评估完成! 模型性能报告已保存至 {report_path}") except Exception as e: print(f"❌ 评估失败: {str(e)}") def display(self): """显示交互界面""" display(self.control_panel) # 在Jupyter Notebook中运行以下代码来启动交互式评估器 # evaluator = ModelEvaluator() # evaluator.display() ``` ## Jupyter Notebook 使用指南 ### 1. 基本使用方式 ```python # 导入必要模块 from module4 import evaluate_model, plot_training_history import torch # 加载训练好的模型 checkpoint = torch.load('poetry_break_model.pth') model = ... # 创建模型实例(需与训练时相同结构) model.load_state_dict(checkpoint['model_state_dict']) model = model.cpu() model.eval() # 加载训练历史 history = checkpoint['history'] # 加载测试数据加载器 test_loader = torch.load('test_data_loader.pt') # 执行评估 test_accuracy = evaluate_model(model, test_loader, history) # 单独绘制训练历史 plot_training_history(history) ``` ### 2. 交互式评估器(推荐) ```python # 创建并显示交互式评估器 evaluator = ModelEvaluator() evaluator.display() ``` 运行此代码后,Notebook 中会出现交互式控制面板: 1. 输入模型路径(默认为 `poetry_break_model.pth`) 2. 输入测试数据加载器路径(默认为 `test_data_loader.pt`) 3. 点击"加载模型和数据"按钮 4. 加载成功后点击"评估模型"按钮 ### 3. 结果解读 评估完成后将显示: 1. **训练历史图表**:包含损失曲线和准确率曲线 2. **分类报告**: ``` precision recall f1-score support 非断句 0.92 0.95 0.94 1200 断句 0.94 0.90 0.92 800 accuracy 0.93 2000 macro avg 0.93 0.93 0.93 2000 weighted avg 0.93 0.93 0.93 2000 ``` 3. **总体准确率**:`0.9300` 4. **性能报告**:保存为 `model_performance.json` ### 4. 可视化分析技巧 在 Jupyter 中可以使用以下技巧进行深入分析: ```python # 分析训练历史 import pandas as pd # 将历史转换为DataFrame history_df = pd.DataFrame(history) # 计算每个epoch的提升幅度 history_df['train_acc_diff'] = history_df['train_acc'].diff() history_df['val_acc_diff'] = history_df['val_acc'].diff() # 找出最佳epoch best_epoch = history_df['val_acc'].idxmax() print(f"最佳验证准确率在第 {best_epoch+1} 轮: {history_df.loc[best_epoch, 'val_acc']:.4f}") # 绘制学习率变化 plt.figure(figsize=(10, 6)) plt.plot(history['lr'], 'g-', linewidth=2) plt.title('学习率变化曲线', fontsize=14) plt.xlabel('Epoch', fontsize=12) plt.ylabel('学习率', fontsize=12) plt.grid(True, linestyle='--', alpha=0.7) plt.show() # 保存详细训练历史 history_df.to_csv('training_history.csv', index_label='epoch') ``` ### 5. 高级功能:模型比较 ```python # 比较多个模型 def compare_models(model_paths): results = [] for path in model_paths: checkpoint = torch.load(path) model = ... # 创建模型实例 model.load_state_dict(checkpoint['model_state_dict']) model.eval() # 评估模型 accuracy = evaluate_model(model, test_loader, history=None) # 收集结果 results.append({ 'model': path, 'accuracy': accuracy, 'final_train_acc': checkpoint['history']['train_acc'][-1], 'final_val_acc': checkpoint['history']['val_acc'][-1] }) # 转换为DataFrame df = pd.DataFrame(results) df = df.sort_values('accuracy', ascending=False) # 可视化比较 plt.figure(figsize=(10, 6)) plt.barh(df['model'], df['accuracy'], color='skyblue') plt.title('模型准确率比较', fontsize=14) plt.xlabel('测试准确率') plt.xlim(0, 1) plt.grid(axis='x', linestyle='--', alpha=0.7) # 添加数值标签 for i, v in enumerate(df['accuracy']): plt.text(v + 0.01, i, f"{v:.4f}", color='black', va='center') plt.tight_layout() plt.show() return df # 使用示例 model_paths = ['model_v1.pth', 'model_v2.pth', 'model_v3.pth'] results_df = compare_models(model_paths) print(results_df) ``` ## 优势总结 1. **交互式体验**:通过直观的控件简化评估流程 2. **专业可视化**:自动生成符合学术标准的图表 3. **详细报告**:提供分类报告和性能摘要 4. **错误处理**:完善的异常处理机制 5. **扩展性强**:支持多个模型比较和深入分析 6. **即用性强**:开箱即用,无需复杂配置
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值