2418. Wormhole Sort

2418. Wormhole Sort

(File IO): input:wormsort.in output:wormsort.out
時間限制: 1000 ms 空間限制: 262144 KB 具體限制
Goto ProblemSet


題目描述

Farmer John 的奶牛們已經厭倦了他對她們每天早上排好序離開牛棚的要求。她們剛剛完成了量子物理學的博士學位,準備將這一過程搞快點。
今天早上,如同往常一樣,Farmer John 的 N 頭編號爲 1…N 的奶牛(1≤N≤10^5),分散在牛棚中 N 個編號爲 1…N 的不同位置,奶牛 i 位於位置 pi。但是今天早上還出現了 M 個編號爲 1…M 的蟲洞(1≤M≤10^5),其中蟲洞 i 雙向連接了位置 ai 和 bi,寬度爲 wi(1≤ai,bi≤N,ai≠bi,1≤wi≤10^9)。
在任何時刻,兩頭位於一個蟲洞兩端的奶牛可以選擇通過蟲洞交換位置。奶牛們需要反覆進行這樣的交換,直到對於 1≤i≤N,奶牛 i 位於位置 i。
奶牛們不想被蟲洞擠壞。幫助她們最大化被她們用來排序的蟲洞寬度的最小值。保證奶牛們有可能排好序。

輸入

輸入的第一行包含兩個整數 N 和 M。
第二行包含 N 個整數 p1,p2,…,pN。保證 p 是 1…N 的一個排列。
對於 1 到 M 之間的每一個 i,第 i+2 行包含整數 ai、bi 和 wi。

輸出

輸出一個整數,爲在排序過程中奶牛必須擠進的蟲洞的最小寬度的最大值。如果奶牛們不需要用任何蟲洞來排序,輸出 −1。

樣例輸入

4 4
3 2 1 4
1 2 9
1 3 7
2 3 10
2 4 3

樣例輸出

9

數據範圍限制

測試點 1-5 滿足 N,M≤1000。
測試點 6-10 沒有額外限制。

提示
​以下是一個僅用寬度至少爲 9 的蟲洞給奶牛排序的可能方案:

奶牛 1 和奶牛 2 使用第三個蟲洞交換位置。
奶牛 1 和奶牛 3 使用第一個蟲洞交換位置。
奶牛 2 和奶牛 3 使用第三個蟲洞交換位置。

主要數據結構:並差集
題目大意:讓第i頭牛回到第p[i]個牛場。

 用並查集就是判斷一下第i頭牛和第p[i]個牛場是否在同一個
 集合(集合內所有的牛都可進行交換),如果他們的祖先相同,
 那麼便能歸位。

方法一

對於輸入數據,先按照蟲洞寬度做一遍降序排列(一旦滿足條件,就一定是最優解),最後只需模擬一下交換的過程即可。
但是不能去每次都判斷牛是否歸位,因爲會超時。
仔細推敲,我們便能知道一條性質:

如果當前點已經歸位,在以後的交換中是不影響的。
明白這個以後,我們就只有統計一下歸位的牛數就行。
時間複雜度:O(m+n);

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
const int MAX=2147483647;
const int N=1e5+10;
struct node
{
	int u,v,w;
} cow[N];
int n,m,p[N],f[N];
bool ff;
void input()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) 
	{
		scanf("%d",&p[i]);   
		if(p[i]!=i) ff=1;  
		f[i]=i;	
	}
	for(int i=1;i<=m;i++) scanf("%d%d%d",&cow[i].u,&cow[i].v,&cow[i].w);
}
bool cmp(node x,node y) {return x.w>y.w;}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
int main()
{
	//fre(wormsort);
	input();
	if(!ff) //特判一開始全都在原位 
	{
		printf("-1");  
		return 0;	
	}
	sort(cow+1,cow+1+m,cmp);
	int j=1;
	for(int i=1;i<=m;i++)
	{
		int a=find(cow[i].u),b=find(cow[i].v);
		if(a!=b) f[a]=f[b]; 
		while(find(p[j])==find(j)) j++;  
		//當一個點已經滿足要求時,那麼在加邊還是滿足要求
		//j巧用數組下標,因爲沒有第0頭牛
		if(j>n) 
		{
			printf("%d\n",cow[i].w);
			return 0;
		}
	}
	return 0;
}

方法二

其實思路與上述一樣,只是我們不需要一個個去枚舉蟲洞當最小距離。
數據滿足有序性,所以可考慮用二分(最小距離)。
但是要注意一點:如果是用降序排序,那麼你的l就應等於最後一個值,r等於第一個值,因爲l<=r。
時間複雜度:O(nlogn);

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
const int MAX=2147483647;
const int N=1e5+10;
struct node
{
	int u,v,w;
} cow[N];
int n,m,p[N],f[N],l,r;
bool ff;
void input()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) 
	{
		scanf("%d",&p[i]);   
		if(p[i]!=i) ff=1;  
	}
	for(int i=1;i<=m;i++) scanf("%d%d%d",&cow[i].u,&cow[i].v,&cow[i].w);
}
bool cmp(node x,node y) {return x.w>y.w;}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
bool check(int minl)
{
	int j=1;
	for(int i=1;i<=n;i++) f[i]=i;	
	for(int i=1;i<=m&&cow[i].w>=minl;i++)
	{
		int a=find(cow[i].u),b=find(cow[i].v);
		f[a]=b;
	}
	for(int i=1;i<=n;i++) if(find(i)!=find(p[i])) return 0;
	return 1;
}
int main()
{
	//fre(wormsort);
	input();
	if(!ff) //特判一開始全都在原位 
	{
		printf("-1");  
		return 0;	
	}
	sort(cow+1,cow+1+m,cmp);
	int l=cow[m].w,r=cow[1].w,mid,ans;
	while(l<=r)
	{
		mid=(l + r) >> 1;
		if(check(mid)) l = mid+1,ans=mid;
		else r = mid-1;
	}
	printf("%d",ans);
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章