HDU 6041 I Curse Myself(点双联通加集合合并求前K大) 2017多校第一场
2024-08-29 21:58:02
题意:
给出一个仙人掌图,然后求他的前K小生成树。
思路:
先给出官方题解
由于图是一个仙人掌,所以显然对于图上的每一个环都需要从环上取出一条边删掉。所以问题就变
为有 M 个集合,每个集合里面都有一堆数字,要从每个集合中选择一个恰好一个数加起
来。求所有的这样的和中,前 K 大的是哪些。这就是一个经典问题了。
点双联通就不说了 都一眼能看出来做法就是缩点之后每个环每次取一个,然后找最大的k个所以这道题的难点就在这里,做法当然是不知道啦,看了题解和博客才懂的。以前做过两个集合合并的,这个是k个合并,和两个集合合并差不多,不过感觉很厉害啊。。就是先两个合并,然后和第三个合并,在和第四个,然后就可以了。。复杂度的话题解说的O(KM)。。。那就O(KM)吧,反正这个是真不会证过程挺厉害的 在合并的时候
void merge(vector<int> &V , vector<int> B) {
priority_queue< opt > Q;
for (int i = 0 ; i < B.size() ; ++ i) {
Q.push((opt) {V[0] + B[i] , i , 0});
}
W.resize(0);
while (W.size() < K && !Q.empty()) {
auto it = Q.top(); Q.pop();
W.push_back(it.w);
if (it.y + 1 < V.size()) {
++ it.y;
Q.push((opt) {B[it.x] + V[it.y] , it.x , it.y});
}
}
V = W;
}
最开始一直没看懂 看了一天呀。。。 后来想通了感觉还是挺简单的,然后在本机跑,没开O2跑了大半辈子。。。
具体过程其实就是对于每个已经合并了的数组,最开始让他最大的和B数组合并,然后对于已经合并了的数组,如果当前
的这一位被push进答案数组,那么就把先合并数组的次大的和B数组的当前数字合并就可以了。
思路挺厉害的~
代码就贴标程的吧~
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int n , m , K; struct opt {
int w , x , y;
bool operator < (const opt& R) const {
return w < R.w;
}
}; vector<int> W;
void merge(vector<int> &V , vector<int> B) {
priority_queue< opt > Q;
for (int i = 0 ; i < B.size() ; ++ i) {
Q.push((opt) {V[0] + B[i] , i , 0});
}
W.resize(0);
while (W.size() < K && !Q.empty()) {
auto it = Q.top(); Q.pop();
W.push_back(it.w);
if (it.y + 1 < V.size()) {
++ it.y;
Q.push((opt) {B[it.x] + V[it.y] , it.x , it.y});
}
}
V = W;
} int pre[N] , mcnt;
struct edge {
int x , w , next;
} e[N << 2]; vector<int> res; int dfn[N] , low[N] , ncnt;
stack<int> S; void dfs(int x , int fa) {
dfn[x] = low[x] = ++ ncnt;
for (int i = pre[x] ; ~i ; i = e[i].next) {
int y = e[i].x;
if (!dfn[y]) {
S.push(i);
dfs(y , i ^ 1);
low[x] = min(low[x] , low[y]);
if (low[y] > dfn[x]) {}//(x , y) is bridge
if (low[y] >= dfn[x]) {
int j;
vector<int> V;
do {
j = S.top();
S.pop();
V.push_back(e[j].w);
} while (j != i);
if (V.size() > 1) {
//cout << V.size() << endl;
//for (auto &x : V) cout << x << ' '; cout << endl;
merge(res , V);
}
}
} else if (i != fa && dfn[y] < dfn[x])
S.push(i) , low[x] = min(low[x] , dfn[y]);
}
} void work() {
memset(pre , -1 , sizeof(pre));
mcnt = ncnt = 0;
int sum = 0;
for (int i = 0 ; i < m ; ++ i) {
int x , y , z;
scanf("%d%d%d" , &x , &y , &z);
e[mcnt] = (edge) {y , z , pre[x]} , pre[x] = mcnt ++;
e[mcnt] = (edge) {x , z , pre[y]} , pre[y] = mcnt ++;
sum += z;
}
scanf("%d" , &K);
res.resize(0);
res.push_back(0);
memset(dfn , 0 , sizeof(dfn));
dfs(1 , 0);
int w = 0;
for (int i = 0 ; i < res.size() ; ++ i) {
w += (i + 1) * (sum - res[i]);
}
static int ca = 0;
printf("Case #%d: %u\n" , ++ ca , w);
} int main() {
res.reserve(100001);
W.reserve(100001);
while (~scanf("%d%d" , &n , &m)) {
work();
}
return 0;
}
最新文章
- Gevent中信号量的使用
- 9月15日,YTFCloud,创业圈的技术新宠
- ELK 部署
- java基础学习01
- ubuntu 14.04 下 安装samba 及SSH 服务端的方法
- android开发之gridlayout使用入门
- WPF采用MVVM模式(绑定:纯前台、命令:触发器绑定命令)
- Oracle SQL函数之转换函数To_char汇总
- CCNA基础知识摘录
- 将表格添加到Word文档中 ,包括表格样式设置
- c#调用腾讯云API的实例
- [转]Golang TLS
- 替换Jar包内的文件
- LSTM长短期记忆神经网络模型简介
- Linux文件的软链接和硬链接
- CentOS 7 使用 Yum 软件源安装谷歌 Chrome 浏览器
- JAVA-JSP内置对象之request对象参数
- pycharm 不显示代码提示
- Appium +Python 连接真机测试
- Html5的placeholder属性(IE兼容)
热门文章
- 极客时间_Vue开发实战_07.Vue组件的核心概念(3):插槽
- POJ - 3253 Fence Repair 优先队列+贪心
- input输入框修改后自动跳到最后一个字符
- 序列化框架MJExtension详解 + iOS ORM框架
- MYSQL5.7版本解决sql_mode=only_full_group_by问题
- C/C++函数调用过程分析
- java 三大基本特征
- mysql引擎问题研究
- 第二篇 Nosql讲解之windows下memcache的安装(一)
- LVS-DR VIP和RIP不同网段的配置方法