当前位置: 首页 > news >正文

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

题目背景

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.xdnf.cn/news/757585.html

相关文章:

  • 继承与多态
  • 篇章七 数据结构——栈和队列
  • 查看make命令执行后涉及的预编译宏定义的值
  • Python数学可视化——环境搭建与基础绘图
  • 力扣刷题(第四十四天)
  • 主数据编码体系全景解析:从基础到高级的编码策略全指南
  • GEE:获取研究区的DEM数据
  • RocketMQ 学习
  • 性能优化 - 案例篇:数据一致性
  • 清理 pycharm 无效解释器
  • CVE-2021-28164源码分析与漏洞复现
  • DDD架构
  • 历年西安邮电大学计算机保研上机真题
  • 鸿蒙OS基于UniApp的区块链钱包开发实践:打造支持鸿蒙生态的Web3应用#三方框架 #Uniapp
  • 基于Dify实现各类报告文章的智能化辅助阅读
  • 攻防 FART 脱壳:特征检测识别 + 对抗绕过全解析
  • C++输入与输出技术详解
  • hot100 -- 5.普通数组系列
  • CFTel:一种基于云雾自动化的鲁棒且可扩展的远程机器人架构
  • Domain Adaptation in Vision-Language Models (2023–2025): A Comprehensive Review
  • 2022—2025年:申博之路及硕士阶段总结
  • 小明的Java面试奇遇之智能家装平台架构设计与JVM调优实战
  • 什么是子查询?相关子查询的性能问题?
  • GpuGeek 618大促引爆AI开发新体验
  • Redis缓存存储:从基础到高阶的深度解析
  • STM32G4 电机外设篇(三) TIM1 发波 和 ADC COMP DAC级联
  • 软件无线电关键技术之正交调制技术
  • Java进阶---JVM
  • GraphQL 入门篇:基础查询语法
  • Cinnamon开始菜单(1):获取应用数据