洛谷P3959 宝藏
去年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 宝藏的更多相关文章
- 洛谷P3959——宝藏
传送门:QAQQAQ 题意: 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了$n$个深埋在地下的宝藏屋, 也给出了这$n$个宝藏屋之间可供开发的$m$条道路和它们的长度. 小明决心亲自前往挖掘所有 ...
- 洛谷P3959 宝藏(NOIP2017)(状压DP,子集DP)
洛谷题目传送门 Dalao的题解多数是什么模拟退火.DFS剪枝.\(O(3^nn^2)\)的状压DP之类.蒟蒻尝试着把状压改进了一下使复杂度降到\(O(3^nn)\). 考虑到每条边的贡献跟它所在的层 ...
- 洛谷 P3959 宝藏 解题报告
P3959 宝藏 题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 \(n\) 个深埋在地下的宝藏屋, 也给出了这 \(n\) 个宝藏屋之间可供开发的 \(m\) 条道路和它们的长度. 小 ...
- 2018.08.09洛谷P3959 宝藏(随机化贪心)
传送门 回想起了自己赛场上乱搜的20分. 好吧现在也就是写了一个随机化贪心就水过去了,不得不说随机化贪心大法好. 代码: #include<bits/stdc++.h> using nam ...
- 洛谷P3959 宝藏(状压dp)
传送门 为什么感觉状压dp都好玄学……FlashHu大佬太强啦…… 设$f_{i,j}$表示当前选的点集为$i$,下一次要加入的点集为$j$时,新加入的点和原有的点之间的最小边权.具体的转移可以枚举$ ...
- 洛谷P3959 宝藏(模拟退火乱搞)
题意 题目链接 题面好长啊...自己看吧.. Sol 自己想了一个退火的思路,没想到第一次交85,多退了几次就A了哈哈哈 首先把没用的边去掉,然后剩下的边从小到大排序 这样我们就得到了一个选边的序列, ...
- 洛谷 P3959 宝藏【状压dp】
一开始状态设计错了-- 设f[i][s]为当前与根节点联通状况为s,最深深度为i 转移的话枚举当前没有和根联通的点集,预处理出把这些点加进联通块的代价(枚举s中的点和当前点的连边乘以i即可),然后用没 ...
- 【题解】洛谷P3959 [NOIP2017TG] 宝藏(状压DP+DFS)
洛谷P3959:https://www.luogu.org/problemnew/show/P3959 前言 NOIP2017时还很弱(现在也很弱 看出来是DP 但是并不会状压DP 现在看来思路并不复 ...
- NOIP2017提高组Day2T2 宝藏 洛谷P3959 状压dp
原文链接https://www.cnblogs.com/zhouzhendong/p/9261079.html 题目传送门 - 洛谷P3959 题目传送门 - Vijos P2032 题意 给定一个 ...
随机推荐
- spring后置处理器BeanPostProcessor
BeanPostProcessor的作用是在调用初始化方法的前后添加一些逻辑,这里初始化方法是指在配置文件中配置init-method,或者实现了InitializingBean接口的afterPro ...
- PermGen space 内存溢出
1.修改D:\tools\tomcat\tomcat - 7\apache-tomcat-7.0.91\bin tomcat 路径下bin 文件的catalina.bat文件 添加 JAVA_OPTS ...
- java学习之—栈
/** * 栈 * Create by Administrator * 2018/6/11 0011 * 上午 10:20 **/ public class StackX { private int ...
- mycat - 水平分表
相对于垂直拆分的区别是:垂直拆分是把不同的表拆到不同的数据库中,而水平拆分是把同一个表拆到不同的数据库中.水平拆分不是将表的数据做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分 ...
- python之路--类的约束, 异常处理, MD5, 日志处理
一 . 类的约束 1. 写一个父类,父类中的某个方法要抛出一个异常 NotImplementedError class Base: # 对子类进行了约束. 必须重写该方法 # 以后上班了. 拿到公司代 ...
- RocketMQ消息队列安装
一.官方安装文档 http://rocketmq.apache.org/docs/quick-start/ 下载地址 https://github.com/apache/rocketmq/releas ...
- Java多线程2:线程的使用及其生命周期
一.线程的使用方式 1.继承Thread类,重写父类的run()方法 优点:实现简单,只需实例化继承类的实例,即可使用线程 缺点:扩展性不足,Java是单继承的语言,如果一个类已经继承了其他类,就无法 ...
- MyBatis的XML中使用内部类的方式
内部类需要使用$符号连接,而不是点.,如 com.pingan.job.openapi.model.SMSESBResult$ReceiveResult$ResultInfo 从CSDN论坛查到的. ...
- peewee 事物 回滚
peewee 事物 回滚 #!/usr/bin/env python # coding=utf-8 from peewee import * db = MySQLDatabase(host='123. ...
- 转 My97日历控件常用功能记录
My97相信大家都不陌生,应该是我所见过的最强大的一个日历控件了,最近的项目中也比较多地用到了此控件,而且项目中经常会有不同时间范围的需求,在此列出一些比较常用的日期范围格式的设置,尽管在My97的官 ...