POJ2449【A*算法】【可持久化左偏树】

K短路模板题.K很小所以A*就可以过了.

/* I will wait for you */

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#include <deque>
#include <set>
#include <map>
#include <string>
#define make make_pair
#define fi first
#define se second

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef map<int, int> mii;

const int maxn = 500010;
const int maxm = 1010;
const int maxs = 26;
const int inf = 0x3f3f3f3f;
const int P = 1000000007;
const double eps = 1e-6;

inline ll read()
{
	ll x = 0, f = 1; char ch = getchar();
	while (ch < '0' || ch > '9')
		  f = (ch == '-' ? -1 : 1), ch = getchar();
	while (ch >= '0' && ch <= '9')
	 	  x = x * 10 + ch - '0', ch = getchar();
	return x * f;
}

typedef pair<int, pii> Pii;

priority_queue<Pii, vector<Pii>, greater<Pii> > q; 

struct edge
{
	int v, w, next;
} e[maxn], _e[maxn];

int n, m, S, T, K, cnt, _cnt, _ans,
	head[maxn], _head[maxn], vis[maxn], h[maxn];

void insert(int u, int v, int w)
{
	e[cnt] = (edge) {v, w, head[u]}, head[u] = cnt++;
}

void _insert(int u, int v, int w)
{
	_e[_cnt] = (edge) {v, w, _head[u]}, _head[u] = _cnt++;
}

void spfa()
{
	for (int i = 1; i <= n; i++)
		h[i] = i == T ? 0 : inf;
	queue<int> _q; _q.push(T);

	while (!_q.empty()) {
		int u = _q.front(); _q.pop(), vis[u] = 0;

		for (int i = _head[u]; ~i; i = _e[i].next) {
			int v = _e[i].v;
			if (h[v] > h[u] + _e[i].w) {
				h[v] = h[u] + _e[i].w;
				if (!vis[v]) vis[v] = 1, _q.push(v);
			}
		}
	}
}

void Astar()
{
	q.push(make(h[S], make(S, 0)));

	while (h[S] != inf && !q.empty() && K) {
		Pii sta = q.top(); q.pop();

		int u = sta.se.fi; double w = sta.se.se;
		if (u == T) _ans = w, K -= 1;

		for (int i = head[u]; ~i; i = e[i].next) {
			int v = e[i].v; double _w = w + e[i].w;
			q.push(make(_w + h[v], make(v, _w)));
		}
	}
}

int main()
{
	scanf("%d%d", &n, &m);

	memset(head, -1, sizeof head);
	memset(_head, -1, sizeof _head);
	for (int i = 1; i <= m; i++) {
		int u, v, w; scanf("%d%d%d", &u, &v, &w);
		insert(u, v, w), _insert(v, u, w);
	}

	scanf("%d%d%d", &S, &T, &K), K += (S == T);

	spfa(), Astar(), printf("%d\n", K ? -1 : _ans);

	return 0;
}

用可持久化堆写了一遍.在K很小的时候似乎和A*时间差别并不大.

但是当K大了以后.A*和可持久化堆根本无法同台竞技.Sigh.

/* I will wait for you */

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#include <deque>
#include <set>
#include <map>
#include <string>
#define make make_pair
#define fi first
#define se second

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef map<int, int> mii;

const int maxn = 10010;
const int maxm = 1000010;
const int maxs = 26;
const int inf = 0x3f3f3f3f;
const int P = 1000000007;
const double eps = 1e-6;

inline ll read()
{
	ll x = 0, f = 1; char ch = getchar();
	while (ch < '0' || ch > '9')
		  f = (ch == '-' ? -1 : 1), ch = getchar();
	while (ch >= '0' && ch <= '9')
	 	  x = x * 10 + ch - '0', ch = getchar();
	return x * f;
}

struct Heap
{
	pii val; int dis; Heap *ls, *rs;
} *null, *H[maxn], *h[maxn];

void init()
{
	null = new Heap();
	null -> val = make(inf, 0);
	null -> dis = -1;
	null -> ls = null -> rs = null;
}

Heap *marge(Heap *a, Heap *b)
{
	if (a == null) return b;
	if (b == null) return a;

	if (a -> val > b -> val) swap(a, b);
	Heap *c = new Heap(); *c = *a;

	c -> rs = marge(a -> rs, b);
	if (c -> rs -> dis > c -> ls -> dis)
		swap(c -> ls, c -> rs);
	c -> dis = c -> rs -> dis + 1;

	return c;
}

Heap *join(Heap *a, pii b)
{
	Heap *c = new Heap();
	c -> val = b, c -> dis = 0;
	c -> ls = c -> rs = null;
	return marge(a, c);
}

struct edge
{
	int v, w, next;
} e[maxm], _e[maxm], Te[maxn];

int n, m, cnt, ans, S, T, K, head[maxn], dis[maxn],
	vis[maxn], _u[maxm], _cnt, _head[maxn], Tcnt,
	Thead[maxn], fa[maxn], delta[maxm];

void insert(int u, int v, int w)
{
	e[cnt] = (edge) {v, w, head[u]}, head[u] = cnt++;
}

void _insert(int u, int v, int w)
{
	_e[_cnt] = (edge) {v, w, _head[u]}, _head[u] = _cnt++;
}

void Tinsert(int u, int v)
{
	Te[Tcnt] = (edge) {v, 0, Thead[u]}, Thead[u] = Tcnt++;
}

void make_heap(int u)
{
	queue<Heap*> _q; h[u] = H[u] = null;
	for (int i = head[u]; ~i; i = e[i].next) {
		pii _v = make(delta[i], i);
		if (e[i].v == fa[u] && !delta[i]) fa[u] = 0;
		else _q.push(join(null, _v));
	}
	while (!_q.empty()) {
		h[u] = _q.front(); _q.pop();
		if (_q.empty()) return;
		Heap *_heap = _q.front();
		h[u] = marge(h[u], _heap);
		_q.pop(), _q.push(h[u]);
	}
}

void spfa()
{
	for (int i = 1; i <= n; i++)
		dis[i] = i == T ? 0 : inf;
	queue<int> _q; _q.push(T);

	while (!_q.empty()) {
		int u = _q.front(); vis[u] = 0, _q.pop();

		for (int i = _head[u]; ~i; i = _e[i].next) {
			int v = _e[i].v;
			if (dis[v] > dis[u] + _e[i].w) {
				dis[v] = dis[u] + _e[i].w, fa[v] = u;
				if (!vis[v]) vis[v] = 1, _q.push(v);
			}
		}
	}

	for (int i = 1; i <= n; i++)
		if (i != T) Tinsert(fa[i], i);
	for (int i = 0; i < cnt; i++)
		delta[i] = e[i].w + dis[e[i].v] - dis[_u[i]];
	for (int i = 1; i <= n; i++)
		make_heap(i);
}

void dfs(int u)
{
	for (int i = Thead[u]; ~i; i = Te[i].next) {
		int v = Te[i].v;
		H[v] = marge(H[u], h[v]), dfs(v);
	}
}

typedef pair<int, pair<int, Heap*> > _Type;
priority_queue<_Type, vector<_Type>, greater<_Type> > q;

void Kshort()
{
	q.push(make(0, make(S, null)));

	while (dis[S] != inf && !q.empty() && K)
	{
		_Type _top = q.top(); q.pop();
		ans = _top.fi, K -= 1;
		int u = _top.se.fi;
		Heap *_heap = _top.se.se;
		int _delta = _heap -> val.fi;

		if (_heap -> ls != null) {
			Heap *_next = _heap -> ls;
			int _pos = e[_next -> val.se].v;
			int _w = _next -> val.fi - _delta;
			q.push(make(ans + _w, make(_pos, _next)));
		}
		if (_heap -> rs != null) {
			Heap *_next = _heap -> rs;
			int _pos = e[_next -> val.se].v;
			int _w = _next -> val.fi - _delta;
			q.push(make(ans + _w, make(_pos, _next)));
		}
		if (H[u] != null) {
			int _pos = e[H[u] -> val.se].v;
			int _w = H[u] -> val.fi;
			q.push(make(ans + _w, make(_pos, H[u])));
		}
	}
}

int main()
{
	n = read(), m = read(), init();

	memset(head, -1, sizeof head);
	memset(_head, -1, sizeof _head);
	memset(Thead, -1, sizeof Thead);

	for (int i = 1; i <= m; i++) {
		int u = read(), v = read();
		int w = read(); _u[cnt] = u;
		insert(u, v, w), _insert(v, u, w);
	}

	S = read(), T = read();
	K = read() + (S == T);
	spfa(), H[T] = h[T], dfs(T), Kshort();

	printf("%d\n", K ? -1 : ans + dis[S]);

	return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-30 17:51:24

POJ2449【A*算法】【可持久化左偏树】的相关文章

【转】左偏树(可合并优先队列)

[可并堆与左偏树] 我们最常用的二叉堆,是最常用的优先队列,它可以在O(logN)内实现插入和删除最小值操作.但是对于合并两个有序的优先队列,二叉堆就显得力不从心了. 左偏树是一种可并堆(Mergeable Heap),意思是可以在O(logN)时间内完成两个堆的合并操作.左偏树(Leftist Tree),或者叫左倾树,左式树,左式堆(Leftist Heap),左堆.顾名思义,它好象是向左偏的,实际上它是一种趋于非常不平衡的二叉树结构,但却能够实现对数级的合并时间复杂度. [左偏树的定义]

左偏树学习

左偏树(Leftist Tree)树这个数据结构内容真的很多,二叉堆,其实就是一颗二叉树,这次讲的左偏树(又叫“左翼堆”),也是树.二叉堆是个很不错的数据结构,因为它非常便于理解,而且仅仅用了一个数组,不会造成额外空间的浪费,但它有个缺点,那就是很难合并两个二叉堆,对于“合并”,“拆分”这种操作,我觉得最方面的还是依靠指针,改变一下指针的值就可以实现,要是涉及到元素的移动,那就复杂一些了.左偏树跟二叉堆比起来,就是一棵真正意义上的树了,具有左右指针,所以空间开销上稍微大一点,但却带来了便于合并的

P1522 派遣 左偏树

左偏树就是一个应该用堆维护的区间,然后需要进行合并操作而发明的算法,其实这个算法没什么难的,和树剖有点像,维护几个数值,然后递归回来的时候就可以修改. 题干: 题目背景 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿. 题目描述 在这个帮派里,有一名忍者被称之为Master.除了Master以外,每名忍者都有且仅有一个上级.为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送. 现在你要招募一批忍者,并把它们

左偏树(p3377)

题目描述 如题,一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作) 操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作) 输入格式 第一行包含两个正整数N.M,分别表示一开始小根堆的个数和接下来操作的个数. 第二行包含N个正整数,其中第i个正整数表示第i个小根堆初始时包含且仅包含的数. 接下来

关于左偏树的一些东东

大概所有的预备知识这里都有https://baike.baidu.com/item/%E5%B7%A6%E5%81%8F%E6%A0%91/2181887?fr=aladdin 例题1:洛谷 P3377 [模板]左偏树(可并堆) 383通过 1.2K提交 题目提供者HansBug 站长团 标签 难度提高+/省选- 时空限制1s / 128MB 提交 讨论 题解 最新讨论更多讨论 加了路径压缩就WA,路过dal… 左偏树用指针写会MLE吗..… m,n写反了也可以过,数据有… 哪位大神有pbds库

左偏树

概要:左偏树是具有左偏性质的堆有序二叉树,它相比于优先队列,能够实现合并堆的功能. 先仪式型orzorzozr国家集训队论文https://wenku.baidu.com/view/515f76e90975f46527d3e1d5.html 左偏树的节点定义: 1 struct node { 2 int lc, rc, val, dis; 3 } LTree[maxn]; 左偏树的几个基本性质如下: 节点的键值小于等于它的左右子节点的键值 节点的左子节点的距离不小于右子节点的距离 节点的距离等于

学习笔记——左偏树

左偏树是一个堆,为了实现快速合并的操作,我们可以构造一颗二叉树,并且使右子树尽量简短 什么是左偏呢? 定义:一个左偏树的外节点是一个左子树为空或者右子树为空的节点,对于每一个点定义一个距离dist它为到它子树内外节点的最短距离. 一个合法的左偏树节点需要满足堆性以及它的右子树的dist比左子树的dist小. 为什么要这样呢? 这样右子树的dist是严格控制在logn以内的. 于是我们合并的时候,将另一个左偏树与当前左偏树的右子树合并,这样递归下去,则时间复杂度是O(logn)的. 这就是一颗左偏

poj 3016 K-Monotonic 左偏树 + 贪心 + dp

//poj 3016 K-Monotonic//分析:与2005年集训队论文黄源河提到的题目类似,给定序列a,求一序列b,b不减,且sigma(abs(ai-bi))最小.//思路:去除左偏树(大根堆)一半的节点(向上取整),让左偏树的根节点上存放中位数:每个左偏树的根节点表示一个等值区间//在本题中,我们将一段区间 与 一颗左偏树等同:将求调整给定数列 vi 为不减序列的代价 与 求取数列 bi 等同 1 #include"iostream" 2 #include"cstd

BZOJ 1455 罗马游戏 左偏树

题目大意:给定n个点,每个点有一个权值,提供两种操作: 1.将两个点所在集合合并 2.将一个点所在集合的最小的点删除并输出权值 很裸的可并堆 n<=100W 启发式合并不用想了 左偏树就是快啊~ #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 1001001 using namespace std; struct abcd{ abcd