P3959 宝藏

题目描述

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

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

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

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

新开发一条道路的代价是:L × K

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

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

输入格式

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

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

输出格式

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


Solution

这道题和 灰原 一起想了一下午还是没有想出来(我们果然还是太菜了),去某谷看了这篇题解,觉得写的很棒想法也很巧,想记录一下这道题来加深一下记忆。

我们已经想到的:1.用一个数来表示走过的点的集合

        2.往这个集合里面加一个点时,枚举此集合里的所有点,取

         代价最小的连边

卡住我们的点是:1.我们无法证明 当往一个集合里新添一个点的时候,只用在

         原来的连边方式上新连一条边即为最优解

        2.无法仅通过一个集合来计算出代价,因为代价中含有边的

         权值和两点之间的距离(或者说是在树上的两点之间的高

         度

题解比我们多想的是:1.引进了一个新的变量-树的高度,这样的好处是,可以

             方便得出代价

          2.在转移时,对于一个已经走了了 i 个点的集合,不一

             定仅从走了 i - 1 个点的集合转移过来,可以一次性从

             i 的子集转移过来,加很多点

          3.正因为一次性可以加多个点,那么每一次我们都是

             添的点全部与高度最高的点连这样子得出的状态不

             一定是正确的,但是正确的答案一定会被算出来(原

             因请看4.)

          4.我们假设有集合 i高度为 h,由集合 j 转移过来,

             此时需要在 j 里面新添 k 个点。我们先用枚举的方

             法,求出sum 表示 k 个点与集合里的点的最短边之

             和,然后将 sum 乘以 h,得出代价。很明显的可以

             看出,k 个点,每个点向集合里连的最短边,不一定

             是高度为 h 的点,所以这个状态所存的代价不一定是

             正确的。但是,总有一个树的高度 h0 可以满足,这

           k 个点,所连的最短边相对应的另外 k 个点,全部是

            高度为 h0 的点(可以自己 举具体的例子一步一步往

            前推),则f[i][h0]所存的代价是正确的,而且是最优

            的,这样子原来的 f[i][h] 所存的,虽然不是正确的,

          但它一定不是最优的,所以取答案时绝对不会取到它

Code

#include<bits/stdc++.h>
#define F(i, x, y) for(int i = x; i <= y; ++ i)
using namespace std;
int read();
const int S = ( << );
const int N = ;
const int inf = 0x3f3f3f3f;
int n, m, ans = inf, all;
int u, v, e;
int d[N][N]; //直接用邻接矩阵存加快访问速度
int s[S]; //预处理出对于集合 i 此时还可以往外连那些点
int f[S][N]; //f[i][j]表示集合 i 高度为 j 时的代价(其实也许是个假的233)
int main()
{
n = read(), m = read(), -- n, all = ( << n + ) - ; //为了方便 将点设为 0 ~ n - 1;all 为总状态数
F(i, , n) F(j, , n) d[i][j] = inf;
F(i, , all) F(j, , n) f[i][j] = inf;
F(i, , n) f[( << i)][] = ; //因为可以自选起点,所以这些状态是合法的
F(i, , m)
{
u = read(), v = read(), e = read();
-- u, -- v, d[u][v] = d[v][u] = min(d[u][v], e); //最多给了1000条边,而实际上最多只有72条,取最短的
}
F(i, , all)
F(j, , n)
if((i | ( << j)) != i)
F(k, , n)
if((i | ( << k)) == i && d[j][k] != inf)
s[i] |= ( << j); //预处理出 s[]
F(i, , all)
for(int s0 = (i - ) & i; s0; s0 = (s0 - ) & i) //保证 s0 是 i 的子集
if((s0 | s[s0] | i) == (s[s0] | s0)) //判断 s0 是否可以经过连边变成 i
{
int sum = ; //求出每条边的权值
F(k, , n)
if((( << k) | i) == i && (( << k) | s0) != s0) //如果 k 不属于 s0 而属于 i,则需要新连边
{
int tmp = inf;
F(h, , n)
if((( << h) | s0) == s0) //如果 h 属于 s0 则可以连边
tmp = min(tmp, d[h][k]);
sum += tmp;
}
F(j, , n)
if(f[s0][j - ] != inf)
f[i][j] = min(f[i][j], f[s0][j - ] + sum * j); //最关键的地方,一定要弄懂!!
}
F(i, , n) ans = min(ans, f[all][i]); //在全集中的每个高度中找最小值
printf("%d\n", ans);
return ;
}
int read()
{
int x = ;
char c = getchar();
while(c < '' || c > '') c = getchar();
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x;
}

【题解】P3959 宝藏 - 状压dp / dfs剪枝的更多相关文章

  1. P3959 宝藏 状压dp

    之前写了一份此题关于模拟退火的方法,现在来补充一下状压dp的方法. 其实直接在dfs中状压比较好想,而且实现也很简单,但是网上有人说这种方法是错的...并不知道哪错了,但是就不写了,找了一个正解. 正 ...

  2. [Luogu P3959] 宝藏 (状压DP+枚举子集)

    题面 传送门:https://www.luogu.org/problemnew/show/P3959 Solution 这道题的是一道很巧妙的状压DP题. 首先,看到数据范围,应该状压DP没错了. 根 ...

  3. LOJ P3959 宝藏 状压dp noip

    https://www.luogu.org/problemnew/show/P3959 考场上我怎么想不出来这么写的,状压白学了. 直接按层次存因为如果某个点在前面存过了则肯定结果更优所以不用在意各点 ...

  4. HDU 4272 LianLianKan (状压DP+DFS)题解

    思路: 用状压DP+DFS遍历查找是否可行.假设一个数为x,那么他最远可以消去的点为x+9,因为x+1~x+4都能被他前面的点消去,所以我们将2进制的范围设为2^10,用0表示已经消去,1表示没有消去 ...

  5. bjtu 1846. Infinity的装备[状压dp+dfs/bfs]

    https://citel.bjtu.edu.cn/acm/oj/problem/1846 1846. Infinity的装备 时间限制 1000 ms 内存限制 64 MB 题目描述 “测试服终于下 ...

  6. 【题解】洛谷P3959 [NOIP2017TG] 宝藏(状压DP+DFS)

    洛谷P3959:https://www.luogu.org/problemnew/show/P3959 前言 NOIP2017时还很弱(现在也很弱 看出来是DP 但是并不会状压DP 现在看来思路并不复 ...

  7. NOIP2017 宝藏 题解报告【状压dp】

    题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度. 小明决心亲自前往挖掘所有宝藏屋中的宝藏.但是 ...

  8. 洛谷$P3959\ [NOIp2017]$ 宝藏 状压$dp$

    正解:状压$dp$ 解题报告: 传送门$QwQ$ $8102$年的时候就想搞这题了,,,$9102$了$gql$终于开始做这题了$kk$ 发现有意义的状态只有当前选的点集和深度,所以设$f_{i,j} ...

  9. BZOJ-1087 互不侵犯King 状压DP+DFS预处理

    1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec Memory Limit: 162 MB Submit: 2337 Solved: 1366 [Submit][ ...

随机推荐

  1. 2,MapReduce原理及源码解读

    MapReduce原理及源码解读 目录 MapReduce原理及源码解读 一.分片 灵魂拷问:为什么要分片? 1.1 对谁分片 1.2 长度是否为0 1.3 是否可以分片 1.4 分片的大小 1.5 ...

  2. 最新SCI影响因子发布!Nature屠榜,AI领域Top 1000期刊盘点

    [导读]2018年度SCI期刊影响因子最新发布,Nature.Science.Cell三大神刊排名前列.新智元摘取其中有关人工智能.机器学习.计算机视觉.机器人学等领域的期刊并做简要介绍,希望对读者选 ...

  3. 不可思议的hexo,五分钟教你免费搭一个高逼格技术博客

    引言 作为程序员拥有一个属于自己的个人技术博客,绝对是百利无一害的事,不仅方便出门装b,面试时亮出博客地址也会让面试官对你的好感度倍增.经常能在很多大佬的技术文章的文末,看到这样一句话: " ...

  4. 小案例带你揭秘JS事件

    小案例带你揭秘JS事件 ### 什么是事件? 在js中一个事件的组成由那些呢? 谁触发事件:事件源 触发什么事件: 事件的类型 触发事件干什么事:事件处理函数 事件传播的过程 捕获阶段 就是从wind ...

  5. Material Design 组件之 FloatingActionButton

    Material Design 设计规范在 Google I/O 2014 推出,这种设计理念一经推出就受到广大开发者的喜爱,主要侧重于纸墨化创作和突出设计的实体感,使得设计更接近于真实世界,力求平滑 ...

  6. 微信小程序生成带参数的二维码(小程序码)独家asp.net的服务端c#完整代码

    一)我先用的小程序端的wx.request去调用API,发现竟然是一个坑! wx.request({ url: 'https://api.weixin.qq.com/wxa/getwxacodeunl ...

  7. spring-cloud-gateway降级

    前言 本文主要研究一下 spring cloud gateway 如何集成 hystrix. 当下游接口负载很大,或者接口不通等其他原因导致超时,如果接口不熔断的话将会影响到下游接口得不到喘息,网关也 ...

  8. 自动由@3x图片生成@2x和@1x的图片 - Xcode插件

    原文:http://www.cocoachina.com/bbs/read.php?tid=277187 生成@3x图片对应的@2x和@1x版本--RTImageAssets 关键字:Xcode插件, ...

  9. 在vue项目中封装echarts的正确姿势

    为什么需要封装echarts 每个开发者在制作图表时都需要从头到尾书写一遍完整的option配置,十分冗余 在同一个项目中,各类图表设计十分相似,甚至是相同,没必要一直做重复工作 可能有一些开发者忘记 ...

  10. PTA | 1019 数字黑洞 (20分)

    给定任一个各位数字不完全相同的 4 位正整数,如果我们先把 4 个数字按非递增排序,再按非递减排序,然后用第 1 个数字减第 2 个数字,将得到一个新的数字.一直重复这样做,我们很快会停在有" ...