資料結構與演算法 雜湊函式和雜湊表等 2

2022-06-09 14:33:13 字數 4210 閱讀 1963

s 城現有兩座監獄,一共關押著n 名罪犯,編號分別為1~n。他們之間的關係自然也極不和諧。很多罪犯之間甚至積怨已久,如果客觀條件具備則隨時可能爆發衝突。我們用「怨氣值」(乙個正整數值)來表示某兩名罪犯之間的仇恨程度,怨氣值越大,則這兩名罪犯之間的積怨越多。如果兩名怨氣值為c 的罪犯被關押在同一監獄,他們倆之間會發生摩擦,並造成影響力為c 的衝突事件。

每年年末,警察局會將本年內監獄中的所有衝突事件按影響力從大到小排成乙個列表,然後上報到s 城z 市長那裡。公務繁忙的z 市長只會去看列表中的第乙個事件的影響力,如果影響很壞,他就會考慮撤換警察局長。

在詳細考察了 n 名罪犯間的矛盾關係後,警察局長覺得壓力巨大。他準備將罪犯們在兩座監獄內重新分配,以求產生的衝突事件影響力都較小,從而保住自己的烏紗帽。假設只要處於同一監獄內的某兩個罪犯間有仇恨,那麼他們一定會在每年的某個時候發生摩擦。那麼,應如何分配罪犯,才能使 z 市長看到的那個衝突事件的影響力最小?這個最小值是多少?

輸入描述:

第一行為兩個正整數n和m,分別表示罪犯的數目以及存在仇恨的罪犯對數。 

接下來的m行每行為三個正整數aj,bj,cj,表示aj號和bj號罪犯之間存在仇恨,其怨氣值為cj。

資料保證1≤aj<bj≤n,0<cj≤1,000,000,000,且每對罪犯組合只出現一次。

輸出描述:
共1行,為z市長看到的那個衝突事件的影響力。如果本年內監獄中未發生任何衝突事件,請輸出0。
示例1

4 6

1 4 2534

2 3 3512

1 2 28351

1 3 6618

2 4 1805

3 4 12884

3512
罪犯之間的怨氣值如下面左圖所示,右圖所示為罪犯的分配方法,市長看到的衝突事件影響力是3512(由2號和3號罪犯引發)。其他任何分法都不會比這個分法更優。
備註:
對於30%的資料有n≤15。

對於70%的資料有n≤2000,m≤50000。

對於100%的資料有n≤20000,m≤100000。

這道題是經典的帶權並查集,可以首先貪心,從怨氣大的對開始放置囚犯,將已放置的囚犯放進並查集中,更新他們與父節點之間的關係,即用權值表示與父節點是否在同乙個監獄內。如果當前的兩個囚犯在同乙個並查集中,那麼檢查他們與根節點(此時他們的父節點也就是根節點)的關係,如果都為與根節點在同乙個監獄或者在不同的監獄,那麼這兩個囚犯被關押在同乙個監獄,將發生衝突,此時輸出當前囚犯對的積怨,就是最優的代價。

#include #include #include #define max 20005

using namespace std;

struct node;

int cmp(node a, node b)

int parent[max] = {};

int relation[max] = {};

// 用relation[x]記錄x和parent[x]關押監獄的情況

// 0表示關在不同的監獄,1表示關在相同的監獄,發生衝突

void init(int n)

} int find(int x)

} int main()

sort(ip.begin(), ip.end(), cmp);

int ans = 0;

for (int i=0; i這題是並查集的乙個變題,先按積怨值從大到小排序,然後乙個乙個看能否完全分開,遇到的第乙個不能分開的囚犯對(如果強行分開就必然有更高的積怨值出現)就是答案。

一開始想到的是按監獄數量弄個並查集,後來發現並不行,因為如果要分開一對囚犯,沒辦法決定誰一定住1號監獄,誰一定住2號監獄。後來試了下用囚犯數量弄並查集,發現也不行,因為沒有積怨的才能放乙個集合裡,比如1和2有積怨不在一起,3和4有積怨不在一起,那1和3,1和4等等就沒法確定,那把他們都放不同集合裡?不行,因為不在乙個集合的可能有積怨可能沒積怨。

讓後我就想當選取到i + 1對囚犯時,前i對囚犯必然也形成一張圖,這張圖可能不是連通的,換句話說,就是包含多個極大聯通子圖(囚犯小團體),小團體與小團體之間互相沒有積怨,因為程式已經選取到了i + 1對囚犯,所以這些小團體內部必然可以兩兩分開以致於沒有積怨。選取其中乙個小團體,如果這個極大聯通子圖沒有壞,那必然可以變形成如下形式:

也就是說,肯定可以一刀切。

如果有環,那促成環的這條線的兩端必分別屬於左右兩邊:

如果這個時候來了一條邊,它的兩個端點都在這張圖的一邊:

那這張圖必然怎麼切都切不開了。

也就是說,如果第i + 1對囚犯都屬於某乙個小團體的一邊,答案就出來了。

也就是說每名囚犯應該有2個狀態i和i',上面的圖應該是這樣:

於是並查集的長度應該為2n,前n個表示1n,後n個表示1'n'。

ps:**中並沒有判斷是否在都一邊而是直接判斷在不在乙個集合,這是因為是直接查詢的同一邊的點,就是沒有'的那邊,這樣直接判斷在不在乙個集合就可以了。

#include using namespace std;

#define rep(i,n) for (int i = 0; i < (n); ++i)

#define for(i,s,t) for (int i = (s); i <= (t); ++i)

#define rfor(i,t,s) for (int i = (t); i >= (s); --i)

#define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)

#define rforeach(i,c) for (__typeof(c.rbegin()) i = c.rbegin(); i != c.rend(); ++i)

#define pr(x) cout << #x << " = " << x << " "

#define prln(x) cout << #x << " = " << x << endl

#define all(x) x.begin(),x.end()

#define ins(x) inserter(x,x.begin())

#define ms0(a) memset(a,0,sizeof(a))

#define msi(a) memset(a,inf,sizeof(a))

#define pii pair#define piii pair,int>

#define mp make_pair

#define pb push_back

#define fi first

#define se second

inline int gc()

inline int ri()

typedef long long ll;

const int maxn = 1e5 + 7;

struct edge

edge(int x, int y, int w) : x(x), y(y), w(w) {}

bool operator < (const edge &x) const

};int n, m, ans;

edge e[maxn];

int f[maxn];

int find(int x)

int main()

sort(e+1, e+m+1);

for(i, 1, n<<1) f[i] = i;

for(i, 1, m)

f[x] = find(e[i].y + n);

f[y] = find(e[i].x + n);

}printf("%d\n", ans);

return 0;

}

資料結構與演算法 雜湊函式與雜湊表

1.雜湊函式的特點 1 理論上輸入域是無窮大的,輸出域是固定的。2 雜湊一般是16位的,每一位是0 9 a f 的隨機數所以一共16的16次方種也就是2 64種 3 雜湊函式輸出的值會均勻分布。比如說輸入域是0 98,輸出域是0 2,我們輸入99個數字,那麼所有輸入的值 3後,0 1 2後面基本都有...

重溫資料結構 雜湊 雜湊函式 雜湊表

hash 雜湊 又稱 雜湊 雜湊 hash 英文原意是 混雜 拼湊 重新表述 的意思。在某種程度上,雜湊是與排序相反的一種操作,排序是將集合中的元素按照某種方式比如字典順序排列在一起,而雜湊通過計算雜湊值,打破元素之間原有的關係,使集合中的元素按照雜湊函式的分類進行排列。在介紹一些集合時,我們總強調...

資料結構與演算法 雜湊表

對映在數學上相當於乙個函式f x a b。雜湊表的核心是乙個雜湊函式 hash function 這個函式規定了集合a中的元素如何對應到集合b中的元素。根據設定的雜湊函式 h key 和處理衝突的方法 將一組關鍵字映像到乙個有限的連續的位址集 區間 上,並以關鍵字在位址集中的 像 作為相應記錄在表中...