啟發式演算法是什麼?
啟發式演算法是基於人類的經驗和直觀感覺,對一些演算法的優化。比如說啟發式搜尋\(a\)*演算法。
啟發式合併是什麼?
考慮乙個問題:把\(n\)個總元素個數為\(m\)的資料結構合併起來(假設是線性的)。
每次合併複雜度最壞\(o(m)\),總複雜度\(o(nm)\)?顯然無法接受。
每次把個數少的合併到個數多的?複雜度\(o(min(m_1, m_2))\)
好像沒啥用?
可是我們注意到,每次合併後個數為合併前少的部分的個數的兩倍以上,每個元素最多合併\(logm\)次,總複雜度\(o(mlogm)\)。
我們也可以啟發式合併更加高階的資料結構,如\(heap\),\(set\),\(splay\)等,複雜度\(o(mlog^2m)\)
很玄學?但這個複雜度分析是對的,而且跑的也快。
例題:hnoi2009 夢幻布丁
題意就不贅述了。
對於每乙個顏色,建一條鍊錶。然後染色就是把鏈短的合併到鏈長的。
需要注意細節,如果把\(2\)染成\(3\),但\(2\)的鏈比\(3\)的長,就需要把\(3\)的合併到\(2\)上。但是現在本應屬於\(3\)的鏈在\(2\)上,就需要記乙個該顏色的鏈現在在哪個顏色上,即是**中的\(now\)陣列。
#include#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
void swap(int &x, int &y)
const int n=1000005;
int head[n], nxt[n], col[n], now[n], cnt[n], st[n], ans;
inline int read()
void merge(int x, int y)
for (int i=head[x]; i; i=nxt[i]) col[i]=y;
nxt[st[x]]=head[y]; head[y]=head[x];
head[x]=st[x]=cnt[x]=0;
}int main()
rep(i, 1, m)
}return 0;
}
在看一道新鮮出爐的聯考題:春節十二響
題意比較複雜,自己看吧$qaq $。
考慮鏈的部分分做法,將兩條支鏈分別排序,然後從大到小加上兩邊的\(max\)即可。
那麼我們就有了乙個暴力做法。對每個點維護乙個堆,每次像鏈那樣暴力合併即可,複雜度大概是\(o(n^2logn)\)?
改成啟發式合併就可以了,每次把小的堆合併到大的堆上。時間複雜度\(o(nlog^2n)\),其實跑的很快。
#include#include#include#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
const int n=200005;
vectorg[n], q;
priority_queueq[n];
int val[n], id[n], n; long long ans;
inline int read()
void dfs(int u)
{ for (int v: g[u])
{dfs(v);
if (q[id[u]].size()本來這裡還有乙個題,經提醒並不是啟發式合併,故刪去
啟發式合併
啟發式合併 暴力合併 將兩個資料結構合併,只需要將小的資料結構中的元素乙個乙個的插入大的資料結構o n o n o n 如果題目只有插入操作沒有 總o n logn o nlogn o nlog n 因為每次合併,所有資料結構總大小為n,設兩個資料結構大小為a,b a b a,b a b a,b a...
啟發式合併
includeconst int n 5e5 5 int f n d n r n p n int find int i int unionn int i,int j int main 並查集 按秩啟發式合併 bzoj4668 冷戰 題目大意 給出n個軍工廠和m 個操作,操作分為兩類 0 u v,這次...
啟發式合併
啟發式合併本質上是一種優化的暴力,可用於擁有穩定結構的資料結構。考慮夢幻布丁 hnoi2009 顯然的暴力思路是用鍊錶維護每種顏色的位置,然後每次修改的時候暴力合併兩條鏈。不難證明,這樣的最壞時間複雜度將達到 o n 2 不能接受。可以觀察到,合併的時間複雜度只與被合併的鏈長度有關,所以可以想到優化...