Description

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

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

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

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

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

$$\mathrm{L} \times \mathrm{K}$$

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

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

Input

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

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

Output

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

Sample Input

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

Sample Output

4

Hint

对于$ 100\%$的数据: $1 \le n \le 12$,$0 \le m \le 1000$,$v \le 500000$

Solution

看到数据范围就知道大概是个状压了。考虑一下怎么设计状态。

看懂题意,题目的意思就是找一棵生成树,使得代价和最小。

考虑在任意时刻,我们关心的只有我们已经把多少点加进生成树了,以及生成树的最大树高是多少。

那么我们就设\(f_{S,i}\)为当前生成树已经包含集合S中的点,并且树高是i。

那么状态转移方程就出来了:

\(f_{S,i}=min\){\(f_{S_0,i-1}+pay\)},其中满足\(S_0\)是\(S\)的子集,通过\(S_0\)加边一定可以联结成\(S\)。\(pay\)是这次加边的花费。

那么怎么判断\(S_0\)在转移中是否合法呢?我们设\(G_S\)是S能拓展到的边的集合,显然G是可以预处理出来的。

在考虑pay的计算。

设\(ss=S~xor~S_0\),即\(ss\)是在\(S\)但不在\(S_0\)中的元素。

这里pay的计算显然是对于每个\(ss\)中的元素取\(S_0\)中的元素向它连一条最短的边求和后\(\times\)i。

考虑这么做为什么是对的。

对于一个集合S和\(S_0\),如果他们之间的边不是被\(S_0\)中最大深度的点连接成的,那么一定存在另一个\(S_1\),包含另一种连边的情况使得\(S_1\)包含除被最大深度点连接的点以外的所有点,那么通过\(S_1\)转移的答案就是最小值,一定是正确的。所以不会漏解。

现在来整理一下思路:

1、预处理出对于每个点的集合所能拓展的点的集合。

2、对于每个点显然把他作为通向地面的点的时候他的深度是0,集合是\((1<<i)\)。那么这样的DP值初始化为0

3、枚举集合,对于每个集合的子集,通过\(G_S\)判断该子集是否合法,如果合法,枚举所有需要被连向的点的最小边权求和乘深度,作为答案。

4、答案就是全集在1~n深度的最小值。

下面考虑时间复杂度:

首先考虑我们枚举集合的数量:

我们集合的枚举量显然是全集的子集的子集个数和。乍一看全集有\(2^n\)个子集,每个子集有\(2^n\)个子集。那么个数是\(O(4^n)\)?事实上不是这样。考虑对于所有的的子集,他们的子集个数是\(O(2^k)\)个,其中\(k\)是子集的元素个数。那么对于大部分的子集,他们的元素个数k都不等于n。显然这个上界太松了。那么如何计算枚举量呢?考虑对于元素个数为x的子集,共有\(C_{n}^x\)种方式。每个子集有\(2^x\)个子集,那么总的枚举量是\(C_{n}^x~\times~2^x\)。应用二项式定理,原式=\((2+1)^n\)=\(3^n\)。所以子集的枚举量是\(O(3^n)\)。

对于每个集合最多有\(n\)个元素,每个元素枚举到它的边是\(O(n)\)的,所以这里的复杂度是\(O(n^2)\)的。

那么总的复杂度是\(O(3^n~\times~n^2)\)。大概是七千万左右。考虑对于绝大部分集合跑不满\(n^2\)的上界,并且可以进行一些剪枝优化,最终可以通过本题

Code

为了二进制运用方便,读入点的时候减一,所有的点从0编号到n-1。

#include<cstdio>
#include<cstring>
#define rg register
#define ci const int
#define cl const long long int typedef long long int ll; namespace IO {
char buf[50];
} template<typename T>
inline void qr(T &x) {
char ch=getchar(),lst=' ';
while(ch>'9'||ch<'0') lst=ch,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if (lst=='-') x=-x;
} template<typename T>
inline void write(T x,const char aft,const bool pt) {
if(x<0) {putchar('-');x=-x;}
int top=0;
do {
IO::buf[++top]=x%10+'0';
x/=10;
} while(x);
while(top) putchar(IO::buf[top--]);
if(pt) putchar(aft);
} template <typename T>
inline T mmax(const T a,const T b) {if(a>b) return a;return b;}
template <typename T>
inline T mmin(const T a,const T b) {if(a<b) return a;return b;}
template <typename T>
inline T mabs(const T a) {if(a<0) return -a;return a;} template <typename T>
inline void mswap(T &a,T &b) {T temp=a;a=b;b=temp;} const int maxn = 15;
const int maxm = 1010;
const int maxt = 1<<maxn;
const int INF = 0x3f3f3f3f; int n,m,a,b,c,ans=INF;
int frog[maxt][maxn],gorf[maxt],dis[maxn][maxn]; int main() {
qr(n);qr(m);
memset(dis,0x3f,sizeof dis);
for(rg int i=1;i<=m;++i) {
a=b=c=0;qr(a);qr(b);qr(c);--a;--b;
dis[b][a]=dis[a][b]=mmin(dis[a][b],c);
}
memset(frog,0x3f,sizeof frog);
int all=(1<<n)-1;
for(rg int i=1;i<=all;++i) {
for(rg int j=0;j<n;++j) if(((1<<j)|i) == i) {
dis[j][j]=0;
for(rg int k=0;k<n;++k) if(dis[j][k]!=INF) {
gorf[i]|=(1<<k);
}
}
}
for(rg int i=0;i<n;++i) frog[1<<i][0]=0;
for(rg int i=2;i<=all;++i) {
for(rg int s0=i-1;s0;s0=(s0-1)&i) if((gorf[s0]|i) == gorf[s0]) {
int _sum=0;
int ss=s0^i;
for(rg int k=0;k<n;++k) if((1<<k)&ss) {
int _temp=INF;
for(rg int h=0;h<n;++h) if((1<<h)&s0) {
_temp=mmin(_temp,dis[h][k]);
}
_sum+=_temp;
}
for(rg int j=1;j<n;++j) if(frog[s0][j-1]!=INF) {
frog[i][j]=mmin(frog[i][j],frog[s0][j-1]+_sum*j);
}
}
}
for(rg int i=0;i<n;++i) ans=mmin(ans,frog[all][i]);
write(ans,'\n',true);
return 0;
}

Summary

1、n个元素的子集的子集数量是\(3^n\)

2、在进行状态设计时可以考虑对于每个时刻需要知道的信息,以此设计维度

3、预处理大法吼啊!

【状压DP】【P3959】【NOIP2017D2T2】宝藏的更多相关文章

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

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

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

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

  3. P3959 宝藏 状压dp

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

  4. 洛谷P3959 宝藏(NOIP2017)(状压DP,子集DP)

    洛谷题目传送门 Dalao的题解多数是什么模拟退火.DFS剪枝.\(O(3^nn^2)\)的状压DP之类.蒟蒻尝试着把状压改进了一下使复杂度降到\(O(3^nn)\). 考虑到每条边的贡献跟它所在的层 ...

  5. NOIp2017D2T2(luogu3959) 宝藏 (状压dp)

    时隔多年终于把这道题锅过了 数据范围显然用搜索剪枝状压dp. 可以记还有哪些点没到(或者已到了哪些点).我们最深已到的是哪些点.这些点的深度是多少,然后一层一层地往下推. 但其实是没必要记最深的那一层 ...

  6. 洛谷P3959 宝藏(状压dp)

    传送门 为什么感觉状压dp都好玄学……FlashHu大佬太强啦…… 设$f_{i,j}$表示当前选的点集为$i$,下一次要加入的点集为$j$时,新加入的点和原有的点之间的最小边权.具体的转移可以枚举$ ...

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

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

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

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

  9. [NOIP2017]宝藏 状压DP

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

  10. NOIP2017宝藏 [搜索/状压dp]

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

随机推荐

  1. spring java config 初探

    Java Config 注解 spring java config作为同xml配置形式的另一种表达形式,使用的场景越来越多,在新版本的spring boot中 大量使用,今天我们来看下用到的主要注解有 ...

  2. EasyUI学习心得

    因为要修改十几年前的一个项目界面,打9月份开始学习EasyUI,很多事情都要自己试过才知道,小问题会浪费很多时间.所以,就在此记录一下,随时更新. 一.引号 EasyUI的自定义关键字的识别,API文 ...

  3. 机器学习-线性回归LinearRegression

    概述 今天要说一下机器学习中大多数书籍第一个讲的(有的可能是KNN)模型-线性回归.说起线性回归,首先要介绍一下机器学习中的两个常见的问题:回归任务和分类任务.那什么是回归任务和分类任务呢?简单的来说 ...

  4. Map Reduce Application(Top 10 IDs base on their value)

    Top 10 IDs base on their value First , we need to set the reduce to 1. For each map task, it is not ...

  5. mac上golang编译出现clang错误

    错误现象 几周前,突然发现我的go 项目编译开始报一种以前从来没有出现过的错误: # runtime/cgo clang: warning: argument unused during compil ...

  6. 一次大量TIME_WAIT和Recv-Q 堵塞问题排查思路

    记录一下周末出现问题~     仅自己摘记不做任何参考. 第一天故障: 现象: 公司销售群和售后群炸了,说老后台(1.0版本)崩溃了,因为还有部门的业务没来得及迁移到新后台,我当时正在打农药哈哈~ 后 ...

  7. 硬件电路中VCC,VDD,VEE,VSS有什么区别

    电路中GND和GROUND.VCC,VDD,VEE,VSS有什么区别 一.解释 DCpower一般是指带实际电压的源,其他的都是标号(在有些仿真软件中默认的把标号和源相连的)VDD:电源电压(单极器件 ...

  8. MFC常用数据类型

    下面这些是和Win32程序共同使用的数据类型BOOL:布尔值,取值为TRUE or FALSEBSTR:32-bit 字符指针BYTE:8-bit整数,未带正负号COLORREF:32-bit数值,代 ...

  9. 第十六次ScrumMeeting会议

    第十六次Scrum Meeting 时间:2017/12/6 地点:线上+SPR咖啡馆 人员:蔡帜 王子铭 游心 解小锐 王辰昱 李金奇 杨森 陈鑫 照片: 目前工作进展 名字 今日 明天的工作 遇到 ...

  10. 常用算法Java实现之冒泡排序

    冒泡排序是所有排序算法中最基本.最简单的一种.思想就是交换排序,通过比较和交换相邻的数据来达到排序的目的. 具体流程如下: 1.对要排序的数组中的数据,依次比较相邻的两个数据的大小. 2.如果前面的数 ...