loj2318 「NOIP2017」宝藏[状压DP]
附带其他做法参考:随机化(模拟退火、爬山等等等)配合搜索剪枝食用。
首先题意相当于在图上找一颗生成树并确定根,使得每个点与父亲的连边的权乘以各自深度的总和最小。即$\sum\limits_{i}depth_i\times value_{i→fa}$。
看数据范围想状压,固定好一个点为根,然后每个点选没选看做状态$0/1$压位,于是朴素思想是$f[S][S_0][d]$表示已经选了$S$,当前$d$层选了$S'$($S'\subset S$),这样一定可以保证由$S'$导出第$d+1$层,更新答案,而$S$内部其他$x\not\in S'$则不影响更新答案。但是,这样做时间和空间双炸,原因在于$S'$的枚举耗费大量时间。真的有枚举的必要吗?如果把状态设计为$f[S][d]$可以否?如果这样,每次从与$S$联通的剩余点里选子集来更新$f[S|T][d+1]$,选的这些点与已选的连通块中的点的最短边可以预处理出来,如果这种最短边恰好连到了第$d$层,那就是合法的。那么直接$mincost(S,T)*(d+1)$代价更新即可。但是如果真正连到的是非第$d$层的话,这样答案会被多算。不过,这样算出的比实际答案劣,一定存在另一种方案,在之前就已经把这个点选上,从而得出更优解,也就是说这个劣解不会影响答案,最优解总是会被枚举出来并且来更新的,这么做相当于把劣解和最优解全部枚举一遍,$min$一定被最优解更新掉了。所以这个$S'$维度可省。只要枚举$S$,在枚举剩余点子集$T$并计算代价,更新即可。
由于每个点都可以作为起始的根,于是$f[2^i][0]$都初始化为$0$。不过本人后来有点不清楚的是为什么不会撞到一起?比如不同根相同$S$和$d$的怎么处理?发现不影响,这样的话会取一个最优的,剩余点集显然与最优的相连才保证代价可能继续最优。具体的话感觉还是要感性理解。
总结:简化维度时要利用性质,推出某些性质来简化,不是凭空去掉的
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define dbg(x) cerr << #x << " = " << x <<endl
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,):;}
template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,):;}
template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int INF=0x3f3f3f3f;
int dis[<<][],arr[<<];
int f[<<][];
int a[][];
int n,m,ans=INF;
int s[],t[];
inline void preprocess(){
for(register int i=;i<(<<n)-;++i){
s[]=t[]=;
for(register int j=;j<=n;++j)
if(i&(<<j-))s[++s[]]=j;
else t[++t[]]=j;
for(register int j=;j<=t[];++j)
for(register int k=;k<=s[];++k)
MIN(dis[i][t[j]],a[t[j]][s[k]]);
for(register int j=;j<=t[];++j)if(dis[i][t[j]]<INF)arr[i]|=<<t[j]-;
}
}
inline void dp(){
for(register int i=;i<=n;++i)f[<<i-][]=;
for(register int d=;d<n;++d)
for(register int S=;S<(<<n)-;++S)if(f[S][d]<INF)
for(register int j=arr[S];j;j=(j-)&arr[S]){
int res=;
for(register int k=;k<=n;++k)if((<<k-)&j)res+=dis[S][k];
MIN(f[S|j][d+],f[S][d]+res*(d+));
}
} int main(){//freopen("treasure.in","r",stdin);//freopen("treasure.out","w",stdout);
read(n),read(m);memset(f,0x3f,sizeof f),memset(dis,0x3f,sizeof dis),memset(a,0x3f,sizeof a);
for(register int i=,x,y,z;i<=m;++i)read(x),read(y),read(z),MIN(a[x][y],z),a[y][x]=a[x][y];
preprocess();
dp();
for(register int i=;i<n;++i)MIN(ans,f[(<<n)-][i]);
printf("%d\n",ans);
return ;
}
预处理后枚举$S$并枚举$T$,$T$可以近似看做补集,则这个过程可以看做子集枚举,每次在统计一下子集的$cost$,则复杂度$O(n3^n)$。但是好像被平方的吊打了。。。
loj2318 「NOIP2017」宝藏[状压DP]的更多相关文章
- BZOJ2073 「POI2004」PRZ 状压DP
问题描述 BZOJ2073 题解 发现 \(n \le 16\) ,显然想到状压 设 \(opt[S]\) 代表过河集合为 \(S\) 时,最小时间. 枚举 \(S\) 的子集,进行转移 枚举子集的方 ...
- 【NOIP2017】 宝藏 状压dp
为啥我去年这么菜啊..... 我现在想了$20min$后打了$10min$就过了$qwq$. 我们用$f[i][j]$表示当前深度为$i$,访问了状态$j$中的所有点的最小代价. 显然$f[i][j] ...
- 「NOIP2017」宝藏
「NOIP2017」宝藏 题解 博客阅读效果更佳 又到了一年一度NOIPCSP-S 赛前复习做真题的时间 于是就遇上了这道题 首先观察数据范围 \(1 \le n \le 12\) ,那么极大可能性是 ...
- [NOIP2017]宝藏 状压DP
[NOIP2017]宝藏 题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度. 小明决心亲自前往挖 ...
- 洛谷P3959 宝藏(NOIP2017)(状压DP,子集DP)
洛谷题目传送门 Dalao的题解多数是什么模拟退火.DFS剪枝.\(O(3^nn^2)\)的状压DP之类.蒟蒻尝试着把状压改进了一下使复杂度降到\(O(3^nn)\). 考虑到每条边的贡献跟它所在的层 ...
- 洛谷$P3959\ [NOIp2017]$ 宝藏 状压$dp$
正解:状压$dp$ 解题报告: 传送门$QwQ$ $8102$年的时候就想搞这题了,,,$9102$了$gql$终于开始做这题了$kk$ 发现有意义的状态只有当前选的点集和深度,所以设$f_{i,j} ...
- P3959 宝藏 状压dp
之前写了一份此题关于模拟退火的方法,现在来补充一下状压dp的方法. 其实直接在dfs中状压比较好想,而且实现也很简单,但是网上有人说这种方法是错的...并不知道哪错了,但是就不写了,找了一个正解. 正 ...
- [Luogu P3959] 宝藏 (状压DP+枚举子集)
题面 传送门:https://www.luogu.org/problemnew/show/P3959 Solution 这道题的是一道很巧妙的状压DP题. 首先,看到数据范围,应该状压DP没错了. 根 ...
- NOIp2017D2T2(luogu3959) 宝藏 (状压dp)
时隔多年终于把这道题锅过了 数据范围显然用搜索剪枝状压dp. 可以记还有哪些点没到(或者已到了哪些点).我们最深已到的是哪些点.这些点的深度是多少,然后一层一层地往下推. 但其实是没必要记最深的那一层 ...
随机推荐
- MySQL递归查询父子节点
1.表结构 CREATE TABLE folder( id BIGINT(20) NOT NULL, parent_id BIGINT(20) DEFAULT NULL, PRIMARY KEY id ...
- linux新建文件夹
mkdir -p .... -p ----parents no error if existion, make parent directories as needed
- 【HANA系列】【第四篇】SAP HANA XS使用服务器JavaScript Libraries详解
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列][第四篇]SAP HANA XS ...
- Django-ORM之聚合和分组查询、F和Q查询、事务
聚合查询 聚合对查询的结果进行一步的计算加工. aggregate()是QuerySet 的一个终止子句 ,他的作用是,返回一个包含一些键值对的字典.键的名称是聚合值的标识符,值是计算出来的聚合值.键 ...
- docker随笔
--查看系统内核版本,docker对于centos系统内核版本需要高于3.10uname -r--移除旧的版本sudo yum remove docker \ docker-client \ dock ...
- [转载]Jupyter Notebook 的快捷键
原文:http://blog.csdn.net/lawme/article/details/51034543 Jupyter Notebook 的快捷键 Jupyter Notebook 有两种键盘输 ...
- Andrew算法求二维凸包-学习笔记
凸包的概念 首先,引入凸包的概念: (有点窄的时候...图片右边可能会被吞,拉开图片看就可以了) 大概长这个样子: 那么,给定一些散点,如何快速地求出凸包呢(用在凸包上的点来表示凸包) Andrew算 ...
- 云数据库 MongoDB版
阿里云云数据库MongoDB版是一种安全可靠.可弹性伸缩的云数据库服务,目前支持ReplicaSet和Sharding两种部署架构,通过简单的几步操作即可快速部署.阿里云云数据库MongoDB版是一种 ...
- 从入门到自闭之Python入门
python是一门解释型编程语言 变量名命名的规则: 变量名由字母,数字,下划线组成 变量名不能以数字开头 变量名要具有可描述性 变量名要区分大小写 变量名禁止使用python关键字 变量名不能使用中 ...
- Java 从入门到精通随笔1
1.java中的main方法必须声明为public static void.String args[]是一个字符串类型的数组,是main()方法的参数. 2.基本数据类型Java有8种基本数据类型:数 ...