之前写了一份此题关于模拟退火的方法,现在来补充一下状压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的更多相关文章

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

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

  2. LOJ P3959 宝藏 状压dp noip

    https://www.luogu.org/problemnew/show/P3959 考场上我怎么想不出来这么写的,状压白学了. 直接按层次存因为如果某个点在前面存过了则肯定结果更优所以不用在意各点 ...

  3. 【题解】P3959 宝藏 - 状压dp / dfs剪枝

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

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

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

  5. [NOIP2017]宝藏 状压DP

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

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

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

  7. 计蒜客 宝藏 (状压DP)

    链接 : Here! 思路 : 状压DP. 开始想直接爆搜, T掉了, 然后就采用了状压DP的方法来做. 定义$f[S]$为集合$S$的最小代价, $dis[i]$则记录第$i$个点的"深度 ...

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

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

  9. Luogu 3959 [NOIP2017] 宝藏- 状压dp

    题解 真的想不到这题状压的做法...听说还有跑的飞快的模拟退火,要是现场做绝对滚粗QAQ. 不考虑深度,先预处理出 $pt_{i, S}$ 表示让一个不属于 集合 $S$ 的 点$i$ 与点集 $S$ ...

随机推荐

  1. 牛客多校Round 4

    Soved:3 rank:133 A.Ternay String 欧拉降幂一下 但是反复求phi会超时 但mod是同一个就可以记忆化一下 #include <bits/stdc++.h> ...

  2. Codeforces985E. Pencils and Boxes (单调队列)

    题意:n个数 把他们放进一些盒子里 每个盒子最少放k个数 且最小和最大的差不能大于d 题解:显然排个序 对于当前点 存一下前面有哪些节点可以当作结尾 那么就可以枚举这些点的下一个点作为起点能否和当前点 ...

  3. /etc/updatedb.conf配置文件

    [root@localhost ~]# vi /etc/updatedb.conf PRUNE_BIND_MOUNTS = "yes" PRUNEFS = "9p afs ...

  4. swift中tableview的使用和注意事项

    今天使用swift写了个简单的tableView,语法和用法上跟oc没多大的区别.但是还是有一些细节的地方需要注意一下的. 先上代码 import UIKit class ViewController ...

  5. fread快读+fwrite快速输出

    定义数组 char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf; 读入 #define getchar() (p1==p2&a ...

  6. Linux---shell基本指令

    1. 显示当前目录 pwd wangzhengchao@ubuntu:~$ cd /home/wangzhengchao/Desktop/ wangzhengchao@ubuntu:~/Desktop ...

  7. Hadoop Mapreduce 中的FileInputFormat类的文件切分算法和host选择算法

    文件切分算法 文件切分算法主要用于确定InputSplit的个数以及每个InputSplit对应的数据段. FileInputFormat以文件为单位切分成InputSplit.对于每个文件,由以下三 ...

  8. concepts in Turbulent Flow

    Table of Contents 1. Concepts/Glossary 1.1. Turbulent eddy viscosity ,μt 1.2. Turbulent kinetic ener ...

  9. 程序员如何在百忙中更有效地利用时间,如何不走岔路,不白忙(忙得要有效率,要有收获)-----https://www.cnblogs.com/JavaArchitect/p/9080484.html

    https://www.cnblogs.com/JavaArchitect/p/9080484.html 程序员如何在百忙中更有效地利用时间,如何不走岔路,不白忙(忙得要有效率,要有收获)

  10. 洛谷 P1877 BZOJ 2748 cogs 791 [HAOI2012]音量调节

    题目描述 一个吉他手准备参加一场演出.他不喜欢在演出时始终使用同一个音量,所以他决定每一首歌之前他都需要改变一次音量.在演出开始之前,他已经做好一个列表,里面写着每首歌开始之前他想要改变的音量是多少. ...