《P3959 [NOIP 2017 提高组] 宝藏》

article/2025/8/5 0:34:50

题目背景

NOIP2017 D2T2

题目描述

参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度。

小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远,也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路则相对容易很多。

小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。

在此基础上,小明还需要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路可以 任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏屋之间的道路无需再开发。

新开发一条道路的代价是 L×K。其中 L 代表这条道路的长度,K 代表从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所经过的宝藏屋的数量(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋) 。

请你编写程序为小明选定由赞助商打通的宝藏屋和之后开凿的道路,使得工程总代价最小,并输出这个最小值。

输入格式

第一行两个用空格分离的正整数 n,m,代表宝藏屋的个数和道路数。

接下来 m 行,每行三个用空格分离的正整数,分别是由一条道路连接的两个宝藏屋的编号(编号为 1−n),和这条道路的长度 v。

输出格式

一个正整数,表示最小的总代价。

输入输出样例

输入 #1复制

4 5 
1 2 1 
1 3 3 
1 4 1 
2 3 4 
3 4 1 

输出 #1复制

4

输入 #2复制

4 5 
1 2 1 
1 3 3 
1 4 1 
2 3 4 
3 4 2  

输出 #2复制

5

说明/提示

【样例解释 1】

小明选定让赞助商打通了 1 号宝藏屋。小明开发了道路 1→2,挖掘了 2 号宝藏。开发了道路 1→4,挖掘了 4 号宝藏。还开发了道路 4→3,挖掘了 3 号宝藏。

工程总代价为 1×1+1×1+1×2=4。

【样例解释 2】

小明选定让赞助商打通了 1 号宝藏屋。小明开发了道路 1→2,挖掘了 2 号宝藏。开发了道路 1→3,挖掘了 3 号宝藏。还开发了道路 1→4,挖掘了 4 号宝藏。

工程总代价为 1×1+3×1+1×1=5。

【数据规模与约定】

对于 20% 的数据: 保证输入是一棵树,1≤n≤8,v≤5×103 且所有的 v 都相等。

对于 40% 的数据: 1≤n≤8,0≤m≤103,v≤5×103 且所有的 v 都相等。

对于 70% 的数据: 1≤n≤8,0≤m≤103,v≤5×103。

对于 100% 的数据: 1≤n≤12,0≤m≤103,v≤5×105。


upd 2022.7.27:新增加 50 组 Hack 数据。

代码实现:

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 110;          // 最大节点数
const int MAXM = 2010;         // 最大边数
int graph[MAXN][MAXN];         // 邻接矩阵存储图
int nodeCount, edgeCount;      // 节点数和边数
int i, j, k, x, y, z;          // 循环和临时变量
int minCost = INT_MAX;         // 最小总代价
int startNode;                 // 起始节点

// 模拟退火相关变量
int parent[MAXN];              // 每个节点的父节点
int tempParent[MAXN];          // 临时父节点数组
int oldCost, newCost;          // 旧代价和新代价
int visited[MAXN];             // 访问标记数组
double temperature;            // 模拟退火温度

// 快速读取整数的函数
int read() {
    int value = 0, sign = 1;
    char c = getchar();
    while((c < '0') || (c > '9')) {
        if(c == '-') sign = -1;
        c = getchar();
    }
    while((c >= '0') && (c <= '9')) {
        value = value * 10 + (c - '0');
        c = getchar();
    }
    return value * sign;
}

// 深度优先搜索检查是否形成合法树结构
int dfsCheck(int currentNode, int depth) {
    if(depth > 13) return 0;  // 深度过大,可能存在环
    if(currentNode != startNode) {
        visited[currentNode] = dfsCheck(tempParent[currentNode], depth + 1);
        return visited[currentNode];
    }
    else return 1;  // 回到起点,形成环
}

// 检查当前父节点数组是否构成合法树
int isValidTree() {
    memset(visited, 0, sizeof(visited));
    for(i = 1; i <= nodeCount; i++) {
        if(visited[i] == 0) visited[i] = dfsCheck(i, 1);
        if(visited[i] == 0) return 0;  // 存在无法到达的节点
    }
    return 1;  // 所有节点都在树中
}

// 计算当前树结构的代价
int dfsCost(int currentNode) {
    if(currentNode == startNode || visited[currentNode] != 0) 
        return visited[currentNode];
    int depth = dfsCost(tempParent[currentNode]) + 1;
    visited[currentNode] = depth;
    newCost += depth * graph[tempParent[currentNode]][currentNode];
    return depth;
}

// 计算新的总代价
void calculateNewCost() {
    newCost = 0;
    memset(visited, 0, sizeof(visited));
    for(i = 1; i <= nodeCount; i++) {
        if(visited[i] == 0) dfsCost(i);
    }
}

// 使用BFS构建初始树并计算代价
void bfsBuildTree(int start) {
    int queue[MAXN], head = 1, tail = 1;
    queue[head] = start;
    oldCost = 0;
    parent[start] = 0;  // 根节点的父节点为0
    
    // 初始化哈希数组为-1,表示未访问
    int hash[MAXN];
    memset(hash, -1, sizeof(hash));
    hash[start] = 0;
    
    while(head <= tail) {
        int current = queue[head++];
        for(i = 1; i <= nodeCount; i++) {
            if(hash[i] == -1 && graph[current][i] != INT_MAX) {
                hash[i] = hash[current] + 1;
                queue[++tail] = i;
                parent[i] = current;
                oldCost += graph[current][i] * hash[i];
            }
        }
    }
    minCost = min(minCost, oldCost);
}

int main() {
    // 读取输入
    nodeCount = read();
    edgeCount = read();
    
    // 初始化邻接矩阵
    for(i = 1; i <= nodeCount; i++) {
        for(j = 1; j <= nodeCount; j++) {
            if(i == j) graph[i][j] = 0;
            else graph[i][j] = INT_MAX;
        }
    }
    
    // 读取边信息
    for(i = 1; i <= edgeCount; i++) {
        x = read();
        y = read();
        z = read();
        graph[x][y] = min(graph[x][y], z);
        graph[y][x] = min(graph[y][x], z);
    }
    
    // 初始化随机数种子
    srand(19260817);
    
    // 枚举每个节点作为起点
    for(i = 1; i <= nodeCount; i++) {
        startNode = i;
        bfsBuildTree(i);
        
        // 节点数太少时不进行模拟退火
        if(nodeCount <= 2) continue;
        
        // 模拟退火算法
        for(temperature = 10000; temperature >= 0.00001; temperature *= 0.9999) {
            // 随机选择两个节点
            x = (rand() % (nodeCount - 1)) + 1;
            if(x >= startNode) x++;  // 跳过起始节点
            
            y = (rand() % (nodeCount - 1)) + 1;
            if(y >= x) y++;
            
            // 如果两点之间没有边,跳过
            if(graph[x][y] == INT_MAX) continue;
            
            // 备份当前父节点数组
            for(j = 1; j <= nodeCount; j++) 
                tempParent[j] = parent[j];
            
            // 修改边,尝试新解
            tempParent[x] = y;
            
            // 检查是否形成合法树
            if(!isValidTree()) continue;
            
            // 计算新解的代价
            calculateNewCost();
            
            // 判断是否接受新解
            if(newCost <= oldCost || 
               exp((oldCost - newCost) / temperature) >= ((rand() % 1000000) / 1000000.0)) {
                minCost = min(minCost, newCost);
                for(j = 1; j <= nodeCount; j++) 
                    parent[j] = tempParent[j];
                oldCost = newCost;
            }
        }
    }
    
    printf("%d\n", minCost);
    return 0;
}


http://www.hkcw.cn/article/hxSfmNfMIS.shtml

相关文章

59、干系人概述

干系人&#xff08;Stakeholders&#xff09;是指在项目、组织、活动或任何特定情境中&#xff0c;具有利益、影响力或受其影响的人、团体或组织。他们可以是内部的&#xff08;如项目团队成员、管理层&#xff09;&#xff0c;也可以是外部的&#xff08;如客户、供应商、政府…

计算机视觉---YOLOv5

YOLOv5理论讲解 一、YOLOv5 整体架构解析 YOLOv5 延续了 YOLO 系列的 单阶段目标检测框架&#xff0c;包含 主干网络&#xff08;Backbone&#xff09;、颈部网络&#xff08;Neck&#xff09; 和 检测头&#xff08;Head&#xff09;&#xff0c;但在结构设计上更注重 轻量化…

前端框架进化史

本内容是对 You’ll Never Manually Update the DOM Again // Here’s Why 内容的翻译与整理。 你再也不需要手工更新DOM, 以下是原因 现代 JavaScript 框架&#xff0c;如 React、Vue、Svelte、Solid、Quick&#xff0c;以及本周推出的其他 786 个框架&#xff0c;都试图做一些…

Redis笔记

Redis&#xff08;Remote Dictionary Server&#xff09;&#xff0c;开源、基于C语言、内存可持久化的NoSQL的键值对数据库。 命令&#xff1a;redis命令不区分大小写&#xff0c;set和SET效果相同 主键&#xff08;key&#xff09;&#xff1a;任意二进制序列&#xff08;字…

flask pyinstaller打包exe,出现module not found问题

最近大作业要做一个项目要打包成可执行程序,这里说一下这个module not found问题,并提供几种可能的方案,如果严格按照这些来走就能解决常见问题,剩下的神仙问题建议问问ai或者清缓存重试 首先说一下目录问题,这应该是包括我(打包app.py)在内的大多数人遇见该报错问题的原因,提…

基于SpringBoot+Redis实现RabbitMQ幂等性设计,解决MQ重复消费问题

一、实现方案 本实验方案参考「RabbitMQ消息可靠性深度解析&#xff5c;从零构建高可靠消息系统的实战指南」 1、业务层幂等处理&#xff1a; 每个消息携带一个全局唯一ID&#xff0c;在业务处理过程中&#xff0c;首先检查这个ID是否已经被处理过。例如&#xff0c;将已处理消…

性能优化 - 案例篇:数据一致性

文章目录 Pre引言1. 分布式缓存概念2. Redis 与 Memcached 区别概览3. Spring Boot 中使用 Redis3.1 引入依赖与常用客户端3.2 RedisTemplate 的基本用法3.3 Spring Cache 注解式缓存 4. 秒杀业务简介及挑战5. Lua 脚本实现原子库存扣减5.1 准备阶段&#xff1a;数据预加载5.2 …

【深度学习】 19. 生成模型:Diffusion Models

Diffusion Models Diffusion Models 简介 Diffusion 模型是一类通过逐步添加噪声并再逆向还原的方式进行图像生成的深度生成模型。其基本流程包括&#xff1a; 前向过程&#xff08;Forward Process&#xff09;&#xff1a;将真实图像逐步加噪&#xff0c;最终变为高斯噪声…

【速通RAG实战:进阶】22、RAG 技术前沿探索:GraphRAG 等 13 种技术详解与应用场景

一、RAG技术的演进脉络与前沿分类 (一)从基础RAG到前沿创新的技术跃迁 传统RAG(检索增强生成)通过“检索-生成”两阶段解决LLM的知识时效性和准确性问题,但在复杂推理、多模态融合、成本控制等场景面临瓶颈。前沿RAG技术围绕检索精度、推理深度、生成质量、系统效率四大…

美业新动能:智能体如何赋能行业“维护”升级(3/6)

摘要&#xff1a;美业行业蓬勃发展&#xff0c;但竞争激烈、客户要求提高等挑战并存。智能体技术应运而生&#xff0c;它融合机器学习、自然语言处理和计算机视觉等技术&#xff0c;实现精准营销、个性化服务&#xff0c;优化客户关系、设备和供应链维护。本文探讨智能体在美业…

RAGflow详解及实战指南

目录 前言 一、RAGflow核心技术解析 1. 技术原理&#xff1a;检索与生成的协同进化 2. 架构设计&#xff1a;分层模块化与高扩展性 3. 核心优势&#xff1a;精准、高效、安全 二、RAGflow实战应用场景 1. 企业知识库搭建 2. 智能客服系统 3. 投资分析报告生成 4. 制造…

C# winform教程(二)

一、基础控件 常用的基础控件主要有按钮&#xff0c;文本&#xff0c;文本输入&#xff0c;组&#xff0c;进度条&#xff0c;等等。 基础控件 名称含义详细用法Button按钮checkbox多选按钮Combobox下拉选择groupbox组控件label标签&#xff0c;显示文字panel控件集合&#xf…

Altium Disigner(16.1)学习-原理图绘制以及必要操作

一、下载软件 通过网盘分享的文件&#xff1a;Altium Designer 16.zip 链接: https://pan.baidu.com/s/1uBHeoJJ-iA2tXw3NRjCcdA?pwd7c3h 提取码: 7c3h 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 --来自百度网盘超级会员v5的分享 二、建立工程 添加proje…

DAY 18 推断聚类后簇的类型

目录 DAY 18 推断聚类后簇的类型1.推断簇含义的2个思路&#xff1a;先选特征和后选特征2.通过可视化图形借助ai定义簇的含义3.科研逻辑闭环:通过精度判断特征工程价值作业&#xff1a;参考示例代码对心脏病数据集采取类似操作&#xff0c;并且评估特征工程后模型效果有无提升。…

常见的PLC浮点数字节序转换方法

变量的字节序 在 PLC 中&#xff0c;寄存器的长度通常为 16 bit&#xff0c;常见的数据类型有 16bit、32bit长度的 对于 32 bit 长度的数据&#xff0c;比如浮点型&#xff08;在西门子、Codesys 中称为 REAL 型&#xff09;&#xff0c;由于长度较长&#xff0c;在不同平台、…

【Day42】

DAY 42 Grad-CAM与Hook函数 知识点回顾 回调函数lambda函数hook函数的模块钩子和张量钩子Grad-CAM的示例 作业&#xff1a;理解下今天的代码即可 """ Day 42: Grad-CAM与Hook函数本节主要内容&#xff1a; 1. 回调函数(Callback)和lambda函数- 回调函数是作为参…

各种乱码问题解决措施

1.乱码问题原因 1.数据的编码和解码使用的不是同一个字符集 编码就是我们能看懂的数据转换为计算机能够识别的二进制编码 解码就是将存在计算机里面的二进制编码的文件转化为我们能够识别的字符等内容。 2.使用了不支持某个语言文字的字符集。 1,1html文件乱码 在<meta c…

c++ 异常处理

测试代码&#xff1a; #include <exception>void testException() { // test exceptionclass MyException : public exception {public:using std::exception::exception; // 关键&#xff1a;继承父类所有构造函数};struct Divide {int operator() (int a, int b) cons…

yolo个人深入理解

卷积层的理解,通过云端服务器训练模型,模型构建的重要性,针对极低像素的处理,模型训练召回率提高技巧,卷积层2,4,8,16,32的小模型与大模型的理解 一.关于backbone,neck,head深入理解 1,backbone的主要组成部分是sppf和conv,这是backbone的核心,其中yolov5和yolov8…

Git基本操作

目录 1. 创建Git本地仓库 2. 配置Git 3. 认识工作区、暂存区、版本库 4. 添加文件 -- 场景一 5. 查看.git文件 6. 添加文件 -- 场景二 7. 修改文件 8. 版本回退 9. 撤销修改 9.1 情况一&#xff1a;对于工作区的代码&#xff0c;还没有add 9.2 情况二&#xff1a;已…