NOIP2017提高组day2 - T2:宝藏

题目链接

[NOIP2017 提高组] 宝藏

题目描述

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

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

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

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

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

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

输入格式

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

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

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

4

样例 #2

样例输入 #2

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

样例输出 #2

5

提示

在这里插入图片描述

【样例解释 1 1 1

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

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

【样例解释 2 2 2

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

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

【数据规模与约定】

对于 20 % 20\% 20% 的数据: 保证输入是一棵树, 1 ≤ n ≤ 8 1 \le n \le 8 1n8 v ≤ 5 × 1 0 3 v \le 5\times 10^3 v5×103 且所有的 v v v 都相等。

对于 40 % 40\% 40% 的数据: 1 ≤ n ≤ 8 1 \le n \le 8 1n8 0 ≤ m ≤ 1 0 3 0 \le m \le 10^3 0m103 v ≤ 5 × 1 0 3 v \le 5\times 10^3 v5×103 且所有的 v v v 都相等。

对于 70 % 70\% 70% 的数据: 1 ≤ n ≤ 8 1 \le n \le 8 1n8 0 ≤ m ≤ 1 0 3 0 \le m \le 10^3 0m103 v ≤ 5 × 1 0 3 v \le 5\times 10^3 v5×103

对于 100 % 100\% 100% 的数据: 1 ≤ n ≤ 12 1 \le n \le 12 1n12 0 ≤ m ≤ 1 0 3 0 \le m \le 10^3 0m103 v ≤ 5 × 1 0 5 v \le 5\times 10^5 v5×105

算法思想

根据题目描述,小明需要考虑如何开凿宝藏屋之间的道路,并且两个已经被挖掘过的宝藏屋之间的道路无需再开发,也就是说题目求的是一棵生成树,使得代价和最小。

由于开发一条道路的代价与道路的长度 L L L和从起点到宝藏屋所经过的宝藏屋的数量(即宝藏屋的深度 K K K有关,因此不能使用最小生成树的算法进行计算。

考虑到结点数 n n n的范围较小( 1 ≤ n ≤ 12 1 \le n \le 12 1n12),考虑使用状态压缩动态规划解决。

状态表示

S S S表示当前生成树的状态,其二进制位上的 0 0 0 1 1 1表示是否包含相应的宝藏屋。例如,当有 0 、 1 、 2 、 3 、 4 0、1、2、3、4 01234一共 5 5 5个宝藏屋时,目前生成树已经包含了 0 、 3 、 4 0、3、4 034,那么 s t a t e = ( 11001 ) 2 state=(11001)_2 state=(11001)2 s t a t e state state的范围从 0 ∼ 2 n − 1 0\sim 2^n-1 02n1

状态除了跟当前生成树的情况有关,还与树的深度有关,用 i i i表示当前生成树的深度,其中起点的深度为 0 0 0

因此, f [ s t a t e ] [ i ] f[state][i] f[state][i]表示当前生成树状态为 s t a t e state state、并且树的深度为 i i i时,工程总代价的最小值。

最终结果为包含所有宝藏屋时,对于不同深度的生成树取最小值,即 m i n { f [ 2 n − 1 ] [ i ] } min\{f[2^n-1][i]\} min{f[2n1][i]},其中 0 ≤ i < n 0\le i<n 0i<n

状态计算

状态转移

要计算当前状态 f [ s t a t e ] [ i ] f[state][i] f[state][i],从最后一步分析,即从第 i − 1 i-1 i1层可以转移到第 i i i

  • 不妨设第 i i i层点集(宝藏屋)的状态为 s s s s s s是状态 s t a t e state state的一个子集
  • 如果前 i − 1 i-1 i1层的点集的状态为 t t t,那么 t = s t a t e ⊕ s t= state\oplus s t=states(异或运算 x o r xor xor

因此:
f [ s t a t e ] [ i ] = m i n { f [ t ] [ i − 1 ] + c o s t } f[state][i] = min\{f[t][i-1]+cost\} f[state][i]=min{f[t][i1]+cost}

计算代价

其中 c o s t cost cost表示第 i i i层所有点(宝藏屋)到第 i − 1 i-1 i1层的最小代价。那么 c o s t = L × K cost = L\times K cost=L×K,其中 L L L表示第 i i i层所有点到第 i − 1 i-1 i1层的长度之和; K K K代表从起点到第 i i i的深度。

为了能够快速计算 L L L,可以预处理得到任一点到所有集合的最小长度,不妨设 g [ i ] [ s t a t e ] g[i][state] g[i][state]表示点 i i i到点集 s t a t e state state的最小长度。那么 g [ i ] [ s t a t e ] g[i][state] g[i][state]等于点 i i i到点集 s t a t e state state中任意一点的最短距离。

算法流程

枚举所有要计算的点集 s t a t e state state,在计算当前状态 f [ s t a t e ] [ i ] f[state][i] f[state][i]时:

  • 要枚举 s t a t e state state的所有子集,即第 i i i层的点集状态 s s s
    • 计算出第 i i i层的所有点到第 i − 1 i-1 i1层的最小长度之和 L L L
    • 然后再枚举深度 i i i,计算 f [ s t a t e ] [ i ] f[state][i] f[state][i]

初始状态

  • 求的是工程总代价最小值,因此 f f f数组应初始化尽可能大
  • 可以打通任意一个宝藏屋到地面的通道到,因此对于任意一点 i i i,在生成树中只包含该点、且深度为 0 0 0时,其最小值应该为 0 0 0,即 f [ 1 < < i ] [ 0 ] = 0 f[1<<i][0]=0 f[1<<i][0]=0

时间复杂度

时间复杂度包含两部分:

  • 预处理 g [ i ] [ s t a t e ] g[i][state] g[i][state]的时间复杂度为 O ( n 2 × 2 n ) O(n^2\times2^n) O(n2×2n)
    • 状态数为 n × 2 n n\times2^n n×2n
    • 计算过程中需要枚举任意集合 s t a t e state state中任意点,时间复杂度 O ( n ) O(n) O(n)
  • 计算状态 f [ s t a t e ] [ i ] f[state][i] f[state][i]的时间复杂度为 O ( n 2 × 3 n ) O(n^2\times3^n) O(n2×3n)
    • 计算过程中需要枚举集合的所有子集。考虑对于元素个数为 k k k的子集,一共有 C n k C_n^k Cnk种情况,每个子集有 2 k 2^k 2k个子集,那么需要枚举的次数为 ∑ k = 0 n C n k × 2 k \sum_{k=0}^nC_n^k\times2^k k=0nCnk×2k。利用二项式定理: ∑ k = 0 n C n k × 2 k = ( 1 + 2 ) n = 3 n \sum_{k=0}^nC_n^k\times2^k=(1+2)^n=3^n k=0nCnk×2k=(1+2)n=3n
    • 对于每个子集需要 n 2 n^2 n2次计算来算出剩余点到子集中的最小长度。

二项式定理 ( x + y ) n = C n 0 x n y 0 + C n 1 x n − 1 y 1 + C n 2 x ( n − 2 ) y 2 + . . . + C n n − 1 x 1 y n − 1 + C n n x 0 y n (x+y)^n=C_n^0x^ny^0+C_n^1x^{n-1}y^1+C_n^2x^(n-2)y^2+...+C_n^{n-1}x^1y^{n-1}+C_n^nx^0y^n (x+y)n=Cn0xny0+Cn1xn1y1+Cn2x(n2)y2+...+Cnn1x1yn1+Cnnx0yn

代码实现

#include <iostream>
#include <cstring>
using namespace std;

const int N = 12, M = 1 << N, INF = 0x3f3f3f3f;
//g[i][state]表示点i到集合state的最小长度
int w[N][N], g[N][M];
//f[state][i]表示当前生成树状态为state、并且树的深度为i时,工程总代价的最小值
int f[M][N];
int main()
{
    int n, m;
    cin >> n >> m;
    memset(w, 0x3f, sizeof w);
    while(m --)
    {
        int a, b, c;
        cin >> a >> b >> c;
        //点的编号从0开始,以便处理点集
        a --, b --;
        //有重边,所以取最小值
        w[a][b] = w[b][a] = min(w[a][b], c);
    }
    //预处理任一点到所有集合的最小长度
    memset(g, 0x3f, sizeof g);
    for(int i = 0; i < n; i ++)
        for(int state = 0; state < 1 << n; state ++)
            //枚举state中的点
            for(int k = 0; k < n; k ++)
                if(state >> k & 1)
                    //更新i到集合state长度的最小值
                    g[i][state] = min(g[i][state], w[i][k]); 
    //初始状态
    memset(f, 0x3f, sizeof f);
    //以i点为起点到达深度0的最小代价为0
    for(int i = 0; i < n; i ++) f[1 << i][0] = 0;
    //状态计算
    for(int state = 1; state < 1 << n; state ++)
    {
        //枚举state的子集s
        for(int s = state - 1 & state; s != 0; s = s - 1 & state)
        {
            //t表示前i-1层的点集状态
            int t = state ^ s, L = 0;
            //枚举第i层所有点,计算第i层的所有点到第i-1层的最小长度之和L
            for(int k = 0; k < n; k ++)
            {
                if(s >> k & 1) //点k在第i层的集合中
                {
                    L += g[k][t]; //累加最后一层所有点到上一层的长度
                    if(L >= INF) break; //点k到不了第i-1层的所有点
                }
            }
            
            if(L >= INF) continue; //子集s中存在点无法到达第i-1层
            //枚举深度,计算当前状态f[state][i]
            for(int i = 1; i < n; i ++)
                f[state][i] = min(f[state][i], f[t][i - 1] + L * i);
        }
    }
    //结果为包含所有宝藏屋时,对于不同深度的生成树取最小值
    int ans = INF;
    for(int i = 0; i < n; i ++)
        ans = min(ans, f[(1 << n) - 1][i]);
    cout << ans;
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/242158.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HarmonyOS开发入门HelloWorld及工具安装

下载与安装DevEco Studio 在HarmonyOS应用开发学习之前&#xff0c;需要进行一些准备工作&#xff0c;首先需要完成开发工具DevEco Studio的下载与安装以及环境配置。 进入DevEco Studio下载官网&#xff0c;单击“立即下载”进入下载页面。 DevEco Studio提供了Windows版本和…

DeCap DECODING CLIP LATENTS FOR ZERO-SHOT CAPTIONING VIA TEXT-ONLY TRAINING

DeCap: DECODING CLIP LATENTS FOR ZERO-SHOT CAPTIONING VIA TEXT-ONLY TRAINING 论文&#xff1a;https://arxiv.org/abs/2303.03032 代码&#xff1a;https://github.com/dhg-wei/DeCap OpenReview&#xff1a;https://openreview.net/forum?idLt8bMlhiwx2 TL; DR&#xff…

新版Spring Security6.2案例 - Basic HTTP Authentication

前言&#xff1a; 书接上文&#xff0c;翻译官网Authentication的Username/Password这页&#xff0c;接下来继续翻译basic的这页&#xff0c;因为官网说的都是原理性的&#xff0c;这边一个小案例关于basic http authentication。 Basic Authentication 本节介绍 HTTP 基本身…

项目总结-自主HTTP实现

终于是写完了&#xff0c;花费了2周时间&#xff0c;一点一点看&#xff0c;还没有扩展&#xff0c;但是基本功能是已经实现了。利用的是Tcp为网络链接&#xff0c;在其上面又写了http的壳。没有使用epoll&#xff0c;多路转接难度比较高&#xff0c;以后有机会再写&#xff0c…

【程序人生】还记得当初自己为什么选择计算机?

✏️ 初识计算机&#xff1a; 还记得人生中第一次接触计算机编程是在高中&#xff0c;第一门编程语言是Python&#xff08;很可惜由于条件限制的原因&#xff0c;当时没能坚持学下去......现在想来有点后悔&#xff0c;没能坚持&#xff0c;唉......&#xff09;。但是&#xf…

快速上手linux | 一文秒懂Linux各种常用目录命令(上)

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 一 、命令提示符和命令的基本格式1.1 如何查看主机名称及修改 二、命令基本格式2.1 命令格式示例2.2 参数的作用…

电商类app如何进行软件测试?有必要进行第三方软件测试吗?

电商类app在开发过程中&#xff0c;软件测试是一个非常重要的环节。通过软件测试&#xff0c;可以确保app在发布和使用过程中的稳定性和安全性。那么&#xff0c;电商类app究竟如何进行软件测试?是否有必要进行第三方软件测试? 一、电商类app如何进行软件测试?   1. 内部…

【Linux】多线程编程

目录 1. 线程基础知识 2. 线程创建 3. 线程ID&#xff08;TID&#xff09; 4. 线程终止 5. 线程取消 6. 线程等待 7. 线程分离 8. 线程互斥 8.1 初始化互斥量 8.2 销毁互斥量 8.3 互斥量加锁和解锁 9. 可重入和线程安全 10. 线程同步之条件变量 10.1 初始化条件变…

Collecting Application Engine Performance Data 收集应用程序引擎性能数据

You can collect performance data of any specific SQL action of an Application Engine program to address any performance issue. 您可以收集应用程序引擎程序的任何特定SQL操作的性能数据&#xff0c;以解决任何性能问题。 You can collect performance data of the S…

IDEA中工具条中的debug按钮不能用了显示灰色

IDEA中工具条中的debug按钮不能用了显示灰色 1. 问题描述 IDEA上的DEBUG按钮突然变成了灰色&#xff1a; 2. 解决办法 一通搜索&#xff0c;终于找到解决办法 点击 File -> Project Structure如下图操作 3. 重启&#xff0c;解决 4. 参考 https://www.cnblogs.com…

【代码随想录】刷题笔记Day35

前言 日常学习&#xff0c;抵触心理5%&#xff1b;毫无指示的干活&#xff0c;抵触心理95% 122. 买卖股票的最佳时机 II - 力扣&#xff08;LeetCode&#xff09; 把整体利润拆分为每次利润&#xff0c;只要积上涨的就可以&#xff0c;so easy class Solution { public:int …

C++共享和保护——(2)生存期

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 生命如同寓言&#xff0c;其价值不在于…

lv12 uboot概述即SD卡制作

1 开发板启动过程 BL0环境初始化一下 查看拨码开关 BL0把SD卡里的内容复制到内存里面运行&#xff0c;因为直接在SD&#xff08;uboot&#xff09;里是无法运行的&#xff0c;属于外设。 uboot开始运行&#xff0c;初始化软硬件环境 把外存里的rootf、dtb、linux搬到内存&a…

xtu oj 1194 Recipient

题目描述 快递小哥每天都辛苦的送快递&#xff0c;今天他需要送N份快递给N个收件人&#xff0c;第i份快递需要送给第i个收件人。 请问其中发生恰好K个送错了的情况数是多少&#xff1f; 输入 存在多样例。 每行输入两个整数N和K&#xff0c;1≤N≤1000,0≤K≤N。 如果两个都…

SQL必会的常用函数

目录 条件函数 if IF(条件表达式,值1,值2) 如果条件表达式为True&#xff0c;返回值1&#xff0c;为False,返回值2. 返回值可以是任何值&#xff0c;比如&#xff1a;数值&#xff0c;文本&#xff0c;日期&#xff0c;空值&#xff0c;NULL&#xff0c;数学表达式&#xff…

Github入门

简介 github是一个基于git的代码仓库&#xff0c;可以通过git来上传和下载代码。国内类似的有gitee。 开源项目一般会申明开源协议。我们可以基于可商用的代码开发我们自己的项目&#xff0c;以期进行快速开发。 一般情况下gitee上的项目基本都够我们使用了。 git基础 Git…

Java笔记草稿——已完成

导航&#xff1a; 【Java笔记踩坑汇总】Java基础JavaWebSSMSpringBootSpringCloud瑞吉外卖/黑马旅游/谷粒商城/学成在线设计模式面试题汇总性能调优/架构设计源码-CSDN博客 推荐学习视频&#xff1a; 黑马程序员全套Java教程_哔哩哔哩 尚硅谷Java入门视频教程_哔哩哔哩 目录 零…

SOLIDWORKS CSWE认证考试报名

​ SOLIDWORKS CSWE是高级别的SOLIDWORKS认证&#xff0c;是一项充满挑战性的艰巨任务。CSWE测试不是简单注册就可以的&#xff0c;是要有一定资格才能参加考试&#xff0c;您首先需要获得CSWP证书&#xff0c;然后还得通过5个CSWPA系列主题考试中的至少4个主题&#xff08;钣金…

七天搞定软件测试,这一篇教程就够了,学完最少能拿13k

前言 在软件开发的世界中&#xff0c;软件测试是不可或缺的一部分。它是确保软件质量、功能完整性和用户满意度的关键环节。本文小编将为大家介绍各类软件测试的奥秘&#xff0c;并提供入门级的指导和见解。 本文内容概要&#xff1a; 软件测试是什么&#xff1f;黑盒测试vs…

2023-12-13 VsCode + CMake + Qt环境搭建

点击 <C 语言编程核心突破> 快速C语言入门 VsCode CMake Qt环境搭建 前言一、前期准备二、具体设置总结 前言 要解决问题: 最近研究 Qt, 使用 qtcreator, 发现在搭建 UI 界面时候很方便, 但到编码和调试就比较有问题了. 想到的思路: 用 VSCode 进行编码及调试. 其它…