Luogu P3959 宝藏 题解 [ 紫 ] [ 状压 dp ] [ 二项式定理 ]
宝藏:一个对着蓝书代码调都能调两个小时的大毒瘤,但是思路还是很值得借鉴的,有普通状压和三进制状压两种做法,或者暴搜剪枝也可以(这里不介绍暴搜剪枝做法)。
普通状压做法
观察到 \(n\le 12\),首先想到状压。
但考虑到普通的状压不太行,因为 \(K\) 这个数算在代价里,会导致这个 dp 有后效性。同时也观察到最终形成的方案一定是一棵树。
因此,我们尝试把 \(K\) 加入状态中。
定义 \(dp_{K,i}\) 表示这棵树扩展到第 \(K\) 层,状态为 \(i\) 时的最小花费。
那么我们在转移的时候枚举一下 \(i\) 的子集 \(j\),从 \(dp_{K-1,j}+cost\times(K-1)\) 转移就好了。
所以我们预处理的时候要把每个状态的能转移到该状态的子集列出来。
这个操作,我们可以先列出每个状态拓展所有点的边后的状态 \(expd_i\),并且记录下每个状态 \(i\) 扩展第 \(j\) 个点的最小花费 \(road_{i,j}\),目的是便于计算 \(cost\) 的值。
于是我们枚举每一个状态的子集,判断这个状态是否是该子集的 \(expd\) 的子集。如果是,则可以转移,枚举所有需要扩展的点,加上它的 \(road\) 即可。
细节
枚举某个状态的子集
枚举 \(i\) 这个状态的非零子集,可以通过如下代码实现:
for(int j=i;j!=0;j=((j-1)&i))
如果要枚举 \(0\),必须特判;如果不能枚举自身,那么把 \(j\) 初始化重设一下:int j=((i-1)&i)
。
例子:枚举 \(14\) 的非零子集(包括自己)。
因为每次都从某个子集减 \(1\),并且还与了自身,所以保证每次都是子集,且比以前都小,保证了不重、不漏。
位运算
注意优先级,很容易被坑,多打括号。
优先级从大到小:
- * 和 / 乘除
- + 和 - 加减
- >> 和 << 左移右移
- > 和 < 和 == 和 != 和 >= 和 <= 比较符
- ^ (xor) 异或
- | 位或
注意: ! 和 ~ 的优先级很高,不要乱用,甚至高于加减的优先级,尽量加括号使用!!!
时间复杂度分析
二项式定理:
\]
其中,组合数的系数可以看作是杨辉三角里的数,其实这个定理初二就学过。
该定理可以逆用。
那么我们用此来解决本题枚举子集部分的复杂度分析:
\]
这就是 dp 部分复杂度,预处理的复杂度为 \(O(m\times2^n)\)。
总体复杂度 \(O(n\times3^n+m\times2^n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
int n,m,expd[4505];
ll d[15][15],road[4505][15],dp[15][4505],ans=0x3f3f3f3f3f3f3f3f;
vector<pi>frm[4505];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
memset(d,0x3f,sizeof(d));
memset(road,0x3f,sizeof(road));
for(int i=1;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
d[u][v]=d[v][u]=min(1ll*w,d[u][v]);
}
for(int i=1;i<=n;i++)d[i][i]=0;
//初始化expand和road函数
for(int i=0;i<(1<<n);i++)
{
expd[i]=i;
for(int j=1;j<=n;j++)
{
if(((i>>(j-1))&1)==0)continue;//位运算不要偷懒,这里写 !(i>>(j-1))&1 是错的!
road[i][j]=0;
for(int k=1;k<=n;k++)
{
if(((i>>(k-1))&1)==1)continue;
if(d[j][k]>=(0x3f3f3f3f/2))continue;
expd[i]=expd[i]|(1<<(k-1));
road[i][k]=min(road[i][k],d[j][k]);
}
}
}
//初始化每个状态的子集们
for(int i=0;i<(1<<n);i++)
{
for(int j=i;j!=0;j=((j-1)&i))
{
if((i&expd[j])!=i)continue;
ll cst=0;
for(int k=1;k<=n;k++)
{
if(((i^j)>>(k-1))&1)cst+=road[j][k];
}
frm[i].push_back({j,cst});
}
}
//状压dp
memset(dp,0x3f,sizeof(dp));
for(int i=0;i<n;i++)dp[1][1<<i]=0;
for(int i=1;i<=n;i++)//层数
{
for(int j=0;j<(1<<n);j++)//当前状态
{
for(auto tmp:frm[j])
{
int st=tmp.first;
ll cst=tmp.second;
dp[i][j]=min(dp[i][j],dp[i-1][st]+cst*(i-1));// i要-1 ,不要读错题
}
}
}
for(int i=1;i<=n;i++)ans=min(ans,dp[i][(1<<n)-1]);
cout<<ans;
return 0;
}
三进制状压做法
口胡一下,有点难写。
三进制数,该位为 \(0\) 代表没有开辟,\(1\) 代表早就开辟,可以转移;\(2\) 表示刚开辟,不能转移。
这种做法依然要记录层数。除此之外还要记录 \(3\) 的次幂之类的东西,常数极大。
注意转移时只要转移最高位的 \(1\),因为其他位的 \(1\) 以后一定会循环到,避免了重复枚举。跟愤怒的小鸟那题挺像的。
复杂度 \(O(n^2\times3^n)\),代码就不放了。
Luogu P3959 宝藏 题解 [ 紫 ] [ 状压 dp ] [ 二项式定理 ]的更多相关文章
- 【NOIP2017】宝藏 题解(状压DP)
题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 nnn 个深埋在地下的宝藏屋, 也给出了这 nnn 个宝藏屋之间可供开发的m mm 条道路和它们的长度. 小明决心亲自前往挖掘所有宝藏屋中 ...
- bzoj 2669 题解(状压dp+搜索+容斥原理)
这题太难了...看了30篇题解才整明白到底咋回事... 核心思想:状压dp+搜索+容斥 首先我们分析一下,对于一个4*7的棋盘,低点的个数至多只有8个(可以数一数) 这样的话,我们可以进行一个状压,把 ...
- noi省选 [九省联考2018]一双木棋题解(状压dp)
比浙江简单多了........ 题目转送:https://www.luogu.org/problemnew/show/P4363 分析: 我们注意到n和m都很小,考虑一下状压dp. 显然,棋子摆成的形 ...
- BZOJ 1087 题解【状压DP】
1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3112 Solved: 1816[Submit][ ...
- LibreOJ 6177 题解(状压DP)
题面 传送门 分析 刚看到这道题时想的是跟最短哈密顿路类似的二进制状压DP,先用floyd处理距离 但是此题用二进制不够,应该用三进制 0,1,2分别表示未送,正在送,已送完 dp[s][i]表示当前 ...
- 【FZYZOJ】愚人节礼物 题解(状压DP)
前言:麻麻我会写状压DP了! ---------------------------- 题目描述 愚人节到了!可爱的UOI小朋友要给孩子们送礼物(汗-原题不是可爱的打败图么= =..).在平面直角坐标 ...
- 【SCOI2005】互不侵犯 题解(状压DP)
前言:一道状压DP的入门题(可惜我是个DP蒟蒻QAQ) ------------------ 题意简述:求在一个$n*n$的棋盘中放$k$个国王的方案数.注:当在一个格子中放入国王后,以此格为中心的九 ...
- P1278 单词游戏【题解】(状压dp)
单词游戏 题目描述 Io和Ao在玩一个单词游戏. 他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致. 游戏可以从任何一个单词开始. 任何单词禁止说两遍 ...
- 【SCOI2008】奖励关 题解(状压DP+期望)
题目链接 题目大意:给定$n$个宝物,每次随机抛出一个宝物,奖励分数为$p_i$.但如果选这个宝物必须选过它的前置宝物集合.共进行$K$轮问最优策略下的期望. $n\leq 15,-10^6\leq ...
- P3160 [CQOI2012]局部极小值 题解(状压DP+容斥)
题目链接 P3160 [CQOI2012]局部极小值 双倍经验,双倍快乐 解题思路 存下来每个坑(极小值点)的位置,以这个序号进行状态压缩. 显然,\(4*7\)的数据范围让极小值点在8个以内(以下示 ...
随机推荐
- Django之model外键
外键和表关系 外键: 在MySQL中,表有两种引擎,一种是InnoDB,另外一种是myisam.如果使用的是InnoDB引擎,是支持外键约束的.外键的存在使得ORM框架在处理表关系的时候异常的强大.因 ...
- .NET周刊【11月第4期 2024-11-24】
国内文章 C# 入门深度学习:万字长文讲解微积分和梯度下降 https://www.cnblogs.com/whuanle/p/18551532 这篇文章主要介绍了使用 C# 进行深度学习的方法,特别 ...
- 教你自创工作流,赋予AI助理个性化推荐超能力
之前,我们已经完成了工作流的基本流程和整体框架设计,接下来的任务就是进入实际操作和实现阶段.如果有同学对工作流的整体结构还不够熟悉,可以先参考一下这篇文章,帮助你更好地理解和掌握工作流的各个部分: 本 ...
- ESRI资源
AO帮助 http://resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/: 桌面帮助 10.1:http://resources. ...
- Git使用备忘录
定义 分布式版本控制工具 Git四个工作区域 工作区(Working Directory):就是你平时存放项目代码的地方 暂存区(Stage/Index):用于临时存放你的改动,事实上它只是一个文件, ...
- RPM 与 YUM
RPM 与 YUM rpm 包的管理 rpm 用于互联网下载包的打包及安装工具,它包含在某些 Linux 分发版中.它生成具有.RPM 扩展名的文件.RPM是 RedHat Package Manag ...
- Visual Studio C++ 汇编 混合编程
Visual Studio C++ 汇编 混合编程 实验要求 请用汇编语言编写实现GCD递推公式的子程序,对入口和出口参数形式不做要求,但需要用 C 语言函数来获取输入.调用汇编递推子程序,并且用 C ...
- 鸿蒙(HarmonyOS)原生AI能力之文本识别
鸿蒙(HarmonyOS)原生AI能力之文本识别 原生智能介绍 在之前开发中,很多场景我们是通过调用云端的智能能力进行开发.例如文本识别.人脸识别等. 原生即指将一些能力直接集成在本地鸿蒙系统中,通过 ...
- 【Mybatis】学习笔记02:实现简单的查
Mybatis02:简单的查 如果你没先去学 增删改 ,然后直接看这篇记录,我想会有些困难.因为该文写的很粗劣,只是简单的截图.所以没基础的建议先去看 [Mybatis]学习笔记01:连接数据库,实现 ...
- 视频监控推流助手/极低延迟/支持N路批量多线程推流/264和265推流/监控转网页
一.前言说明 搞视频监控开发除了基本的拉流以外,还有个需求是推流,需要将拉到的流重新推流到流媒体服务器,让流媒体服务做转发和负载均衡,这样其他地方只需要问流媒体服务器要视频流即可.为什么拉了又重新推呢 ...