去年NOIP第二毒瘤(并不)的题终于被我攻克了,接下来就只剩noip难度巅峰列队了。

首先说一下三种做法:随机化,状压DP和搜索。

前两种做法我都A了,搜索实在是毒瘤,写鬼啊。

有些带DFS的记忆化搜索版状压DP,我看着感觉有点问题......

就连随机化里面那个贪心我看起来也感觉是错的.....但还是A了。

所以考试的时候不要虚,大胆写随机化,随便混个几十分,说不定就A了。


20分算法:给定一棵树,枚举根即可。

 #include <cstdio>
#include <algorithm> const int N = , M = , INF = 0x7f7f7f7f; int G[N][N], n; int DFS(int x, int k, int f) {
int ans = ;
for(int i = ; i <= n; i++) {
if(G[x][i] && i != f) {
ans += DFS(i, k + , x);
ans += G[x][i] * k;
}
}
return ans;
} int main() {
int m;
scanf("%d%d", &n, &m);
for(int i = , x, y, z; i <= m; i++) {
scanf("%d%d%d", &x, &y, &z);
if(G[x][y]) {
z = std::min(z, G[x][y]);
}
G[x][y] = G[y][x] = z;
} int ans = INF;
for(int i = ; i <= n; i++) {
ans = std::min(ans, DFS(i, , ));
}
printf("%d", ans); return ;
}

20分代码

40分算法:边权相等,据说可以BFS/DFS/prim,反正我搞不出来....

70分算法:直接枚举全排列,然后按照排列顺序依次加点,贪心即可。

这里有个问题:当前点的深度会对后面节点造成影响,所以贪心出来的不一定是这个顺序的最优解。

但是你有很多排列啊,说不定哪一次就搞出正解来了呢?

 #include <cstdio>
#include <algorithm> const int N = , M = , INF = 0x7f7f7f7f; int G[N][N], n, a[N], dis[N]; int main() {
int m;
scanf("%d%d", &n, &m);
for(int i = , x, y, z; i <= m; i++) {
scanf("%d%d%d", &x, &y, &z);
if(G[x][y]) {
z = std::min(z, G[x][y]);
}
G[x][y] = G[y][x] = z;
} for(int i = ; i <= n; i++) {
a[i] = i;
}
int ans = INF;
do {
dis[a[]] = ;
int t_ans = ;
for(int i = ; i <= n; i++) {
bool f = ;
int small = INF, pos;
for(int j = ; j < i; j++) {
if(!G[a[i]][a[j]]) {
continue;
}
f = ;
if(small > dis[a[j]] * G[a[i]][a[j]]) {
small = dis[a[j]] * G[a[i]][a[j]];
pos = j;
}
}
if(!f) {
t_ans = INF;
goto flag;
}
t_ans += small;
dis[a[i]] = dis[a[pos]] + ;
}
flag:
ans = std::min(ans, t_ans);
}while(std::next_permutation(a + , a + n + )); printf("%d", ans);
return ;
}

AC代码

100分算法_随机化:

把70分算法改进一下,不枚举全排列,而是random_shuffle,说不定哪次就搞出正解来了呢?

然后就真A了......考场上还不是送分送到死啊,反向筛人。跑的还贼快...

 #include <cstdio>
#include <algorithm> const int N = , M = , INF = 0x7f7f7f7f; int G[N][N], n, a[N], dis[N]; int main() {
int m;
scanf("%d%d", &n, &m);
for(int i = , x, y, z; i <= m; i++) {
scanf("%d%d%d", &x, &y, &z);
if(G[x][y]) {
z = std::min(z, G[x][y]);
}
G[x][y] = G[y][x] = z;
} for(int i = ; i <= n; i++) {
a[i] = i;
}
int ans = INF, T = ;
while(T--) {
std::random_shuffle(a + , a + n + );
dis[a[]] = ;
int t_ans = ;
for(int i = ; i <= n; i++) {
bool f = ;
int small = INF, pos;
for(int j = ; j < i; j++) {
if(!G[a[i]][a[j]]) {
continue;
}
f = ;
if(small > dis[a[j]] * G[a[i]][a[j]]) {
small = dis[a[j]] * G[a[i]][a[j]];
pos = j;
}
}
if(!f) {
t_ans = INF;
goto flag;
}
t_ans += small;
dis[a[i]] = dis[a[pos]] + ;
}
flag:
ans = std::min(ans, t_ans);
} printf("%d", ans);
return ;
}

AC代码

100分算法_状压DP:

好,这个才是这篇博客的主要目的。

我们发现这个深度很难搞啊...

然后又发现同一个深度的话,边权的加权是一定的。

然后我们考虑把边权搞到状态里去,那就是f[i][j][k]表示根为i,最大深度为j,已选节点状态是k的最小权值。

然后发现根那个维度其实可以不要,所以就是f[i][j]表示最大深度为i,目前状态为j的最小权值。

然后转移就是枚举j的子集k,计算出从k扩展成j的最小权值,乘上i即可。

然后发现上面那个计算k->j的最小权值,我们对于每个i都要做一次,所以可以预处理出来。

然后搞一搞一些奇怪的细节,就A了...

具体见代码。

 #include <cstdio>
#include <cstring>
#include <algorithm> typedef long long LL;
const int N = , M = , INF = 0x7f7f7f7f; int G[N + ][N + ], n;
LL f[N + ][ << N], val[ << N][ << N]; inline void out(int x) {
for(int i = ; i <= ; i++) {
printf("%d", (x >> i) & );
}
printf(" ");
} int main() {
int m;
scanf("%d%d", &n, &m);
for(int i = , x, y, z; i <= m; i++) {
scanf("%d%d%d", &x, &y, &z);
if(G[x][y]) {
z = std::min(z, G[x][y]);
}
G[x][y] = G[y][x] = z;
} int lm = << n;
memset(f, 0x3f, sizeof(f));
memset(val, 0x3f, sizeof(val));
for(int i = ; i < n; i++) {
f[][ << i] = ;
}
for(int i = ; i < lm; i++) {
for(int j = (i - ) & i; j > ; j = (j - ) & i) { // j -> i
int ans = ;
for(int k = ; k < n; k++) {
if(!(i & ( << k)) || (j & ( << k))) {
continue;
}
int small = INF;
for(int l = ; l < n; l++) { // l -> k
if(!(j & ( << l)) || !G[l + ][k + ]) {
continue;
}
small = std::min(small, G[l + ][k + ]);
}
if(small == INF) {
ans = INF;
break;
}
ans += small;
}
val[j][i] = ans;
}
} for(int i = ; i <= n; i++) { // deep
for(int j = ; j < lm; j++) { // state
for(int k = (j - ) & j; k > ; k = (k - ) & j) { // subset
f[i][j] = std::min(f[i][j], f[i - ][k] + val[k][j] * i);
/*printf("%d ", i);
out(k);
out(j);
printf("%lld + %lld \n", f[i - 1][k], val[k][j] * i);*/
}
}
}
LL ans = 1ll * INF * INF;
for(int i = ; i <= n; i++) {
ans = std::min(ans, f[i][lm - ]);
}
printf("%lld", ans);
return ;
}

AC代码

发现上面一列"然后"......语文水平有待提高啊......

洛谷P3959 宝藏的更多相关文章

  1. 洛谷P3959——宝藏

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

  2. 洛谷P3959 宝藏(NOIP2017)(状压DP,子集DP)

    洛谷题目传送门 Dalao的题解多数是什么模拟退火.DFS剪枝.\(O(3^nn^2)\)的状压DP之类.蒟蒻尝试着把状压改进了一下使复杂度降到\(O(3^nn)\). 考虑到每条边的贡献跟它所在的层 ...

  3. 洛谷 P3959 宝藏 解题报告

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

  4. 2018.08.09洛谷P3959 宝藏(随机化贪心)

    传送门 回想起了自己赛场上乱搜的20分. 好吧现在也就是写了一个随机化贪心就水过去了,不得不说随机化贪心大法好. 代码: #include<bits/stdc++.h> using nam ...

  5. 洛谷P3959 宝藏(状压dp)

    传送门 为什么感觉状压dp都好玄学……FlashHu大佬太强啦…… 设$f_{i,j}$表示当前选的点集为$i$,下一次要加入的点集为$j$时,新加入的点和原有的点之间的最小边权.具体的转移可以枚举$ ...

  6. 洛谷P3959 宝藏(模拟退火乱搞)

    题意 题目链接 题面好长啊...自己看吧.. Sol 自己想了一个退火的思路,没想到第一次交85,多退了几次就A了哈哈哈 首先把没用的边去掉,然后剩下的边从小到大排序 这样我们就得到了一个选边的序列, ...

  7. 洛谷 P3959 宝藏【状压dp】

    一开始状态设计错了-- 设f[i][s]为当前与根节点联通状况为s,最深深度为i 转移的话枚举当前没有和根联通的点集,预处理出把这些点加进联通块的代价(枚举s中的点和当前点的连边乘以i即可),然后用没 ...

  8. 【题解】洛谷P3959 [NOIP2017TG] 宝藏(状压DP+DFS)

    洛谷P3959:https://www.luogu.org/problemnew/show/P3959 前言 NOIP2017时还很弱(现在也很弱 看出来是DP 但是并不会状压DP 现在看来思路并不复 ...

  9. NOIP2017提高组Day2T2 宝藏 洛谷P3959 状压dp

    原文链接https://www.cnblogs.com/zhouzhendong/p/9261079.html 题目传送门 - 洛谷P3959 题目传送门 - Vijos P2032 题意 给定一个 ...

随机推荐

  1. scala mkstring

    如果你想要把集合元素转化为字符串,可能还会添加分隔符,前缀,后缀. Solution 使用mkString方法来打印一个集合内容,下面给一个简单的例子: scala> val a = Array ...

  2. hive自定义函数

  3. python爬虫之多线程、多进程、GIL锁

    背景: 我们知道多线程要比多进程效率更高,因为线程存在于进程之内,打开一个进程的话,首先需要开辟内存空间,占用内存空间比线程大.这样想也不怪,比如一个进程用10MB,开10个进程就得100MB的内存空 ...

  4. 读懂掌握 Python logging 模块源码 (附带一些 example)

    搜了一下自己的 Blog 一直缺乏一篇 Python logging 模块的深度使用的文章.其实这个模块非常常用,也有非常多的滥用.所以看看源码来详细记录一篇属于 logging 模块的文章. 整个 ...

  5. /proc/diskstats

    读取磁盘统计信息,如下所示: linux-HpdBKE:~ # cat /proc/diskstats sda sda1 sda2 dm- dm- dm- sda为整个硬盘的统计信息,sda1为第一个 ...

  6. todo项目总结

    vue+webpack项目工程配置 1.vue-loader+webpack项目配置 2.webpack配置项目加载各种静态资源 3.webpack-dev-server的配置和使用 安装: pack ...

  7. java构造器和构建器

    本文摘自:https://blog.csdn.net/wh2827991/article/details/79013115 在实例化一个类的过程中,通常会遇到多个参数的构造函数,但如果有些参数是非必需 ...

  8. html class选择器与id选择器

    class选择器: <p class="p1"> .p1{ color:red; ..... } id选择器: <p id="p2"> ...

  9. react双向事件的绑定

    双向绑定有三步,第一步,触发onChange事件,第二步,拿到input里的值,第三步,使用setState将拿到的值传回到state中. 如何拿到input里的值,可以有两种方法,第一种方法是参数e ...

  10. debug错误

    Description "opt_design" can fail with error messages similar to the following: opt_design ...