P3959 宝藏 状压dp
之前写了一份此题关于模拟退火的方法,现在来补充一下状压dp的方法。
其实直接在dfs中状压比较好想,而且实现也很简单,但是网上有人说这种方法是错的。。。并不知道哪错了,但是就不写了,找了一个正解。
正解的区别在于状态,(树高是啥意思),每次都是从当前状态的子集转移过来。这里用到了快速枚举子集的操作,很值得写一下。
题干:
题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 nnn 个深埋在地下的宝藏屋, 也给出了这 nnn 个宝藏屋之间可供开发的m mm 条道路和它们的长度。 小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远, 也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路 则相对容易很多。 小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某 个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。 在此基础上,小明还需要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路可以 任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路 所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏 屋之间的道路无需再开发。 新开发一条道路的代价是: L×K\mathrm{L} \times \mathrm{K}L×K L代表这条道路的长度,K代表从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所经过的 宝藏屋的数量(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋) 。 请你编写程序为小明选定由赞助商打通的宝藏屋和之后开凿的道路,使得工程总代 价最小,并输出这个最小值。
输入输出格式
输入格式: 第一行两个用空格分离的正整数 n,mn,mn,m,代表宝藏屋的个数和道路数。 接下来 mmm 行,每行三个用空格分离的正整数,分别是由一条道路连接的两个宝藏 屋的编号(编号为 −n1-n1−n),和这条道路的长度 vvv。 输出格式: 一个正整数,表示最小的总代价。 输入输出样例
输入样例#: 复制 输出样例#: 复制 输入样例#: 复制 输出样例#: 复制 说明 【样例解释1】 小明选定让赞助商打通了1 号宝藏屋。小明开发了道路 → \to →,挖掘了 号宝 藏。开发了道路 → \to →,挖掘了 号宝藏。还开发了道路 → \to →,挖掘了3 3号宝 藏。工程总代价为:×+×+×= \times + \times + \times = ×+×+×= 【样例解释2】 小明选定让赞助商打通了1 号宝藏屋。小明开发了道路 → \to →,挖掘了 号宝 藏。开发了道路 → \to →,挖掘了 号宝藏。还开发了道路 → \to →,挖掘了4 4号宝 藏。工程总代价为:×+×+×= \times + \times + \times = ×+×+×= 【数据规模与约定】 对于20% \%%的数据: 保证输入是一棵树,≤n≤ \le n \le ≤n≤,v≤5000v \le 5000v≤ 且所有的 vv v都相等。 对于 %\%%的数据: ≤n≤ \le n \le ≤n≤,≤m≤ \le m \le ≤m≤,v≤5000v \le 5000v≤ 且所有的v v v都相等。 对于70% \%%的数据: ≤n≤ \le n \le ≤n≤,≤m≤ \le m \le ≤m≤,v≤5000v \le 5000v≤ 对于100% \%%的数据: ≤n≤ \le n \le ≤n≤,≤m≤ \le m \le ≤m≤,v≤500000v \le 500000v≤
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define duke(i,a,n) for(int i = a;i <= n;i++)
#define lv(i,a,n) for(int i = a;i >= n;i--)
#define clean(a) memset(a,0,sizeof(a))
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
typedef long long ll;
typedef double db;
template <class T>
void read(T &x)
{
char c;
bool op = ;
while(c = getchar(), c < '' || c > '')
if(c == '-') op = ;
x = c - '';
while(c = getchar(), c >= '' && c <= '')
x = x * + c - '';
if(op) x = -x;
}
template <class T>
void write(T x)
{
if(x < ) putchar('-'), x = -x;
if(x >= ) write(x / );
putchar('' + x % );
}
const int maxn = ;
const int maxm = ;
const int maxt = << maxn;
int n,m,a,b,c,ans=INF;
int frog[maxt][maxn],gorf[maxt],dis[maxn][maxn];
int main()
{
read(n);
read(m);
memset(dis,0x3f,sizeof(dis));
duke(i,,m)
{
int x,y,z;
read(x);read(y);read(z);
x--;y--;
dis[x][y] = dis[y][x] = min(dis[x][y],z);
}
memset(frog,0x3f,sizeof(frog));
duke(i,,( << n) - )
{
duke(j,,n - )
{
if((( << j) | i ) == i)
{
dis[j][j] = ;
duke(k,,n - )
{
if(dis[j][k] != INF)
{
gorf[i] |= ( << k);
}
}
}
}
}
duke(i,,n - )
frog[ << i][] = ;
duke(i,,( << n) - )
{
for(int s0 = i - ; s0; s0 = (s0 - ) & i)
{
if((gorf[s0] | i) == gorf[s0])
{
int sum = ;
int ss = s0 ^ i;
duke(k,,n - )
{
if(( << k) & ss)
{
int temp = INF;
duke(h,,n - )
{
if(( << h) & s0)
temp = min(temp,dis[h][k]);
}
sum += temp;
}
}
duke(j,,n - )
if(frog[s0][j - ] != INF)
{
frog[i][j] = min(frog[i][j],frog[s0][j - ] + sum * j);
}
}
}
}
int ans = INF;
duke(i,,n - )
{
ans = min(ans,frog[( << n) - ][i]);
}
printf("%d\n",ans);
return ;
}
P3959 宝藏 状压dp的更多相关文章
- [Luogu P3959] 宝藏 (状压DP+枚举子集)
题面 传送门:https://www.luogu.org/problemnew/show/P3959 Solution 这道题的是一道很巧妙的状压DP题. 首先,看到数据范围,应该状压DP没错了. 根 ...
- LOJ P3959 宝藏 状压dp noip
https://www.luogu.org/problemnew/show/P3959 考场上我怎么想不出来这么写的,状压白学了. 直接按层次存因为如果某个点在前面存过了则肯定结果更优所以不用在意各点 ...
- 【题解】P3959 宝藏 - 状压dp / dfs剪枝
P3959 宝藏 题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的m 条道路和它们的长度. 小明决心亲自前往挖掘所有宝 ...
- 洛谷$P3959\ [NOIp2017]$ 宝藏 状压$dp$
正解:状压$dp$ 解题报告: 传送门$QwQ$ $8102$年的时候就想搞这题了,,,$9102$了$gql$终于开始做这题了$kk$ 发现有意义的状态只有当前选的点集和深度,所以设$f_{i,j} ...
- [NOIP2017]宝藏 状压DP
[NOIP2017]宝藏 题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度. 小明决心亲自前往挖 ...
- NOIp2017D2T2(luogu3959) 宝藏 (状压dp)
时隔多年终于把这道题锅过了 数据范围显然用搜索剪枝状压dp. 可以记还有哪些点没到(或者已到了哪些点).我们最深已到的是哪些点.这些点的深度是多少,然后一层一层地往下推. 但其实是没必要记最深的那一层 ...
- 计蒜客 宝藏 (状压DP)
链接 : Here! 思路 : 状压DP. 开始想直接爆搜, T掉了, 然后就采用了状压DP的方法来做. 定义$f[S]$为集合$S$的最小代价, $dis[i]$则记录第$i$个点的"深度 ...
- loj2318 「NOIP2017」宝藏[状压DP]
附带其他做法参考:随机化(模拟退火.爬山等等等)配合搜索剪枝食用. 首先题意相当于在图上找一颗生成树并确定根,使得每个点与父亲的连边的权乘以各自深度的总和最小.即$\sum\limits_{i}dep ...
- Luogu 3959 [NOIP2017] 宝藏- 状压dp
题解 真的想不到这题状压的做法...听说还有跑的飞快的模拟退火,要是现场做绝对滚粗QAQ. 不考虑深度,先预处理出 $pt_{i, S}$ 表示让一个不属于 集合 $S$ 的 点$i$ 与点集 $S$ ...
随机推荐
- JavaScipt30(第三个案例)(主要知识点:css变量)
承接上文 https://www.cnblogs.com/wangxi01/p/10641210.html,下面是第三个案例: 附上项目链接: https://github.com/wesbos/Ja ...
- 如何在MONO 3D寻找最短路路径
前段时间有个客户说他们想在我们的3D的机房中找从A点到B点的最短路径,然而在2D中确实有很多成熟的寻路算法,其中A*是最为常见的,而这个Demo也是用的A*算法,以下计算的是从左上角到右下角的最短路径 ...
- ibatis常用16条SQL语句
(1) 输入参数为单个值 <delete id="com.fashionfree.stat.accesslog.deleteMemberAccessLogsBefore" p ...
- 洛谷——P2007 魔方
P2007 魔方 常神牛家的魔方都是3*3*3的三阶魔方,大家都见过. 模拟即可: #include<iostream> #include<cstdio> #include&l ...
- C++ <queue>用法
C++队列可以不需要自己写,有现成的模版类 头文件: #include <queue> #include <iostream> using namespace std; (之前 ...
- idea 如何将本地代码上传到github
1. 首先切换到项目根目录下 执行 git init 2. 点击项目右键->Git->Repository->Remotes->编辑URL 到github代码地址.
- SpringBoot 拦截器--只允许进入登录注册页面,没登录不允许查看其它页面
SpringBoot注册登录(一):User表的设计点击打开链接 SpringBoot注册登录(二):注册---验证码kaptcha的实现点击打开链接 SpringBoot注册登录(三):注册--验证 ...
- 解决使用myeclipse电脑卡的问题
1. 原因:myeclipse会自动更新,因此会占用大量内存 2. 解决方法: (1)window->Perferences->General->Startup and Shutdo ...
- kafka streams
https://docs.confluent.io/current/streams/concepts.html#ktable
- ZOJ——3609 Modular Inverse
Modular Inverse Time Limit: 2 Seconds Memory Limit: 65536 KB The modular modular multiplicative ...