去年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. postgreSQL使用杂谈

    由于当时是在美国安装的postgreSQL ,导致回到上海后使用时,发现时间数据显示不正确. To acomplish the timezone change in Postgres 9.6 you ...

  2. 剑指offer(6)

    题目: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转 ...

  3. python之路--动态传参,作用域,函数嵌套

    一 . 动态传参(重点)  * ,  ** * 与 ** * 在形参位置. * 表示不定参数, 接收的是位置参数 接收到的位置参数的动态传参: 都是元组 def eat(*food): # 在形参这里 ...

  4. elasticsearch的映射

    一.简介: 映射:在创建索引时,可以预先定义字段的类型(映射类型,也就是type,一个索引可以有一个或多个类型)及相关属性. Elasticsearch会根据JSON源数据的基础类型猜测你想要的字段映 ...

  5. jquery和js的几种页面加载函数的方法以及执行顺序

    参考博客:http://www.cnblogs.com/itslives-com/p/4646790.html    https://www.cnblogs.com/james641/p/783837 ...

  6. RuntimeError: cryptography requires setuptools 18.5 or newer, please upgrade to a newer version of setuptool

    setuptool 太老了,更新下: pip install --upgrade setuptools

  7. LODOP循环多任务 同模版只设置不同队列任务名

    LODOP中从PRINT_INIT(或PRINT_INITA)到最后PRINT(或PREVIEW等),是一个任务,关于Lodop中一个任务,以及一个任务中可以包含哪些语句,详细可查看本博客另一篇博文: ...

  8. mysql语句-DDL语句

    SQL分类 1.DDL语句:数据定义语句,用来定义不同的数据段.数据库,表,列,索引等数据表对象,常用语句:create.drop.alter等. 2.DML语句:数据操作语句,用于添加.删除.更新和 ...

  9. node.js 运行机制与简单使用

    一.hello world 1.引入 required 模块 2.创建服务器 3.接收请求与响应请求 var http = require('http'); // 载入http模块 http.crea ...

  10. winserver 2008 R2服务器安装IIS

    winserver 2008 R2 IIS7 安装IIS 打开服务器管理器 选择“角色”,右击添加角色 点击“下一步” 勾选”Web服务器(IIS)“,点击”下一步“ 勾选”常见Http功能.应用程序 ...