「NOIP2017」宝藏 题解

博客阅读效果更佳


又到了一年一度NOIPCSP-S 赛前复习做真题的时间

于是就遇上了这道题


首先观察数据范围 \(1 \le n \le 12\) ,那么极大可能性是状压 \(\texttt{DP}\) 或者 \(\texttt{DFS}\) 爆搜

但由于这题放在了 \(\texttt{DP}\) 列表里面,于是优先考虑状压

简化题意:

从给定的 \(n\) 个点,\(m\) 条边的有重边的无向联通图中,找出一棵生成树,使得题目所求价值最小

从题目给出的建边价值来看,我们发现一条边的价值跟以下几点有关:

  • 根的位置
  • 当前状态下的树的高度
  • 该边的长度

边的长度不能改变,根的位置并不能很好的作为 \(\texttt{DP}\) 时候的阶段,所以我们考虑以树的高度作为DP的阶段

设根的深度为1

设 \(\texttt{f[i][j]}\) 表示 当前树的高度为 \(i\) ,已经选了的点集的集合为 \(j\),那么状态转移方程即为

\[f[i][j]=\min_{k\ \in \ j}(f[i-1][j \ \mathrm{xor} \ k]+dis[j \ \mathrm{xor} \ k][k]\cdot(i-1))
\]

其中异或操作在这里是取补集的意思,\(\texttt{dis[i][j]}\) 表示从 \(i\) 这个已选点集加上下一层将要选的 \(j\) 这个点集所需要的最小花费

那我们应该如何完善 \(\texttt{dis}\) 数组呢

先给出递推式

\[dis[i][j]=dis[i][j \ \mathrm{xor} \ \mathrm{lowbit}(j)]+\min_{k=1}^{n\ \&\& \ (1<<k)\&i}(d[\log_2\mathrm{lowbit}(j)+1][k])
\]

其中 \(d\) 数组表示第 \(i\) 个点到 第 \(j\) 个点的道路长度(没有则为 \(\infty\) ),\(j\) 为 \(i\) 的补集的任一子集

然后从小到大枚举 \(j\) ,就能够保证顺序正确(因为 \(j \ \mathrm{xor} \ \mathrm{lowbit}(j)\) 一定比 \(j\) 要小)

因为每一次更新只涉及到一个点的更改,所以不难得出这样预处理 \(dis\) 数组的正确性

然后,这题就完了

另外还有几点需要注意的

  • 边最好使用邻接矩阵储存,因为有重边,而且请不要将初值赋得太大,这样会导致在进行动态规划求解的同时溢出,从而导致答案错误

  • 如果按照上面那种朴素的做法来进行求解复杂度有可能不能承受,观察发现我们枚举了许多不必要的子集,所以我们可以换一个方式:

    for(int i=S;i;i=(i-1)&S)

    这样的话所有的 \(i\) 就一定是 \(S\) 的子集

    蒟蒻的理解:不等于 \(S\) 的 \(S\) 的子集一定在 \([0,S)\) 中

    然后或运算可以求出在这当中十进制下数字最大的子集 ,设其为 \(P\),然后其余所有的十进制表示比他小的子集都在\([0,P)\) 当中,如此循环求解,自然能够得到所有的子集

  • 关于状态的一点点优化

    容易发现,当树高为 \(i\) 时,至少需要 \(i\) 个节点,所以所有状态中点的个数小于 \(i\) 的(即二进制位上 \(1\) 的个数小于 \(i\) 的),全部可以不用枚举子集,直接跳过,这对时间复杂度又有了进一步的常数优化。 这可以通过预处理得到。

最后贴一下代码,变量名与上面提到的略有不同

#include<bits/stdc++.h>
using namespace std;
const int maxn=12;
int d[maxn+5][maxn+5];
int g[maxn+5][(1<<maxn)+5];
int f[(1<<maxn)+5][(1<<maxn)+5];
int lg[(1<<maxn)+5];//懒
int q[(1<<maxn)+5],cnt;
int sum[(1<<maxn)+5];
int main()
{
memset(g,63,sizeof g);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int n,m;
cin>>n>>m;
for(int i=0;i<n;++i)
lg[1<<i]=i;//预处理,因为懒
for(int i=0;i<12;++i)
for(int j=0;j<12;++j)
d[i][j]=1000000;//赋最大值
for(int i=1;i<=m;++i)
{
int a,b,c;
cin>>a>>b>>c;
--a,--b,d[a][b]=d[b][a]=min(d[a][b],c);
}
int x,S=(1<<n)-1;//全集定义
for(int i=1;i<=S;++i)
{
x=i;
while(x) x&=(x-1),++sum[i];
}//预处理每一个状态上点的个数
for(int i=1;i<=S;++i)
{
cnt=0;
for(int j=S^i;j;j=(j-1)&(S^i)) q[++cnt]=j;//由于这样做子集的顺序是从大到小的,不符合DP的顺序,所以要逆序
for(int j=cnt;j>=1;--j)
{
int u=lg[q[j]&-q[j]],e=1000000;
for(int v=0;v<n;++v)
if(1<<v&i) e=min(d[u][v],e);
f[i][q[j]]=f[i][q[j]^(q[j]&-q[j])]+e;
}
}
for(int i=0;i<n;++i) g[1][1<<i]=0;//初始状态
for(int i=2;i<=n;++i)
for(int j=(1<<i)-1;j<=S;++j)//剪枝,这里i的初始状态跳过了肯定不符合的状态
{
if(sum[j]<i) continue;//剪枝,不满足直接跳过
for(int k=j;k;k=(k-1)&j)
g[i][j]=min(g[i][j],g[i-1][j^k]+f[j^k][k]*(i-1));
}
int ans=(1<<30);
for(int i=1;i<=n;++i) ans=min(ans,g[i][S]);//取最小值
cout<<ans<<endl;
return 0;
}

「NOIP2017」宝藏的更多相关文章

  1. loj2318 「NOIP2017」宝藏[状压DP]

    附带其他做法参考:随机化(模拟退火.爬山等等等)配合搜索剪枝食用. 首先题意相当于在图上找一颗生成树并确定根,使得每个点与父亲的连边的权乘以各自深度的总和最小.即$\sum\limits_{i}dep ...

  2. 「NOIP2017」「LuoguP3959」 宝藏(爆搜

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

  3. 逛公园「NOIP2017」最短路+DP

    大家好我叫蒟蒻,这是我的第一篇信竞题解blog [题目描述] 策策同学特别喜欢逛公园. 公园可以看成一张 \(N\) 个点 \(M\) 条边构成的有向图,且没有自环和重边.其中 \(1\) 号点是公园 ...

  4. 2018.11.01 loj#2319. 「NOIP2017」列队(线段树)

    传送门 唉突然回忆起去年去noipnoipnoip提高组试水然后省二滚粗的悲惨经历... 往事不堪回首. 所以说考场上真的有debuffdebuffdebuff啊!!!虽然当时我也不会权值线段树 这道 ...

  5. LOJ2319. 「NOIP2017」列队【线段树】

    LINK 思路 神仙线段树 你考虑怎么样才能快速维护出答案 首先看看一条链怎么做? 首先很显然的思路是维护每个节点的是否出过队 然后对于重新入队的点 直接在后面暴力vector存一下就可以了 最核心的 ...

  6. LOJ2316. 「NOIP2017」逛公园【DP】【最短路】【思维】

    LINK 思路 因为我想到的根本不是网上的普遍做法 所以常数出奇的大,而且做法极其暴力 可以形容是带优化的大模拟 进入正题: 首先一个很显然的思路是如果在合法的路径网络里面存在零环是有无数组解的 然后 ...

  7. 「NOIP2017」「LuoguP3952」 时间复杂度(模拟,栈

    题目描述 小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写程序 ...

  8. loj2314 「NOIP2017」小凯的疑惑[同余最短路or数论]

    这题以前就被灌输了“打表找规律”的思想,所以一直没有好好想这道题,过了一年还不太会qwq.虽然好像确实很简单,但是还是带着感觉会被嘲讽的心态写这个题解...而且还有一个log做法不会... 法1:(一 ...

  9. 【LOJ2316】「NOIP2017」逛公园

    [题目链接] [点击打开链接] [题目概括] 对给定\(K\),起点\(1\)到终点\(n\)中对长度为\([L,L+K]\)的路径计数. \(L\)为\(1\)到\(n\)的最短路长度. [思路要点 ...

随机推荐

  1. MongoDB学习笔记:文档Crud Shell

    MongoDB学习笔记:文档Crud Shell   文档插入 一.插入语法 db.collection.insertOne() 将单个文档插入到集合中.db.collection.insertMan ...

  2. 基于Linux的TCP网络聊天室

    1.实验项目名称:基于Linux的TCP网络聊天室 2.实验目的:通过TCP完成多用户群聊和私聊功能. 3.实验过程: 通过socket建立用户连接并传送用户输入的信息,分别来写客户端和服务器端,利用 ...

  3. 性能监控之常见 Java Heap Dump 方法

    一.前言 在本文中,我们总结下抓 Java dump 的几种不同方法. Java Heap Dump 是特定时刻 JVM 内存中所有对象的快照.它们对于解决内存泄漏问题和分析 Java 应用程序中的内 ...

  4. 类编程的WAF(上)

    一.复杂的需求 WAF (WEB 应用防火墙) 用来保护 WEB 应用免受来自应用层的攻击.作为防护对象的 WEB 应用,其功能和运行环境往往是复杂且千差万别的,这导致即便防御某个特定的攻击方式时,用 ...

  5. NX二次开发-通过3x3矩阵获取XYZ轴矢量

    函数:UF_CSYS_ask_wcs() 函数说明:通过3x3矩阵获取XYZ轴矢量 用法: 1 #include <uf.h> 2 #include <uf_mtx.h> 3 ...

  6. csp-s模拟测试57(10.2)「天空龙」·「巨神兵」·「太阳神」

    题目是古埃及神话??? A. 天空龙 傻逼模拟,看来没有滑天下之大稽QAQ,也没有打错快读(大雾...) B. 巨神兵 难度爆增,一脸懵比..... 60分状压: 因为是求有向图,关于有向图好像拓扑用 ...

  7. ORA-19504: failed to create file "/u01/backup/db_0_20190603_1" ORA-27038: created file already exists

    1.问题:在用rman进行0级备份时,报错: ORA-19504: failed to create file "/u01/backup/db_0_20190603_1"ORA-2 ...

  8. 第11章 PADS功能使用技巧(1)-最全面

    一.如何走蛇形线? 蛇形线是布线过程中常用的一种走线方式,其主要目的是为了调节延时满足系统时序设计要求,但是设计者应该有这样的认识:蛇形线会破坏信号质量,改变传输延时,布线时要尽量避免使用,因此一块P ...

  9. Qt5实现文本编辑器(附图片转PDF与详细代码)

    1. 功能介绍 老规矩,我们直接上截图 这是主界面 文件菜单界面 编辑菜单界面 设置菜单界面 关于菜单界面 2. 功能演示 设置字体颜色 设置字体大小 点击关于菜单 点击关于Qt菜单 会自动跳转到Qt ...

  10. 2、SpringBoot整合之SpringBoot整合servlet

    SpringBoot整合servlet 一.创建SpringBoot项目,仅选择Web模块即可 二.在POM文件中添加依赖 <!-- 添加servlet依赖模块 --> <depen ...