1. 程式人生 > >題解 luogu P2083 【找人】

題解 luogu P2083 【找人】

蒟蒻學完並查集,看啥都是並查集。

於是我就神奇的用並查集解出了此題

思路就是從1至n*
m掃一圈,將當前房間所在位置與它指向房間merge注意必須把它merge到它指向房間的兒子節點。最後,你就會發現同學家正好是一個根節點

樣例merge後樣子如下,但樣例中無死迴圈情況,這題是可能出現小明反覆繞圈的情況的,所以我們要判定一下。

                2,3
             3,3   2,1
            1,2     1,3
           3,3       1,1
                   2,2  3,1

我們選取的儲存方式為並查集中的 建立補集法

,並查集陣列中1至m為第一層,m+1至2*
m為第二層,以此類推,所以,並查集陣列要有100000個

還有此題中切記不可用路徑壓縮,不然就無法完美重現找的過程。

程式碼如下

#include<bits/stdc++.h>
using namespace std;
int b[1005][100][3];
int bcj[99999999];
int hp=0,n,m,v,t=0,o=0;
void fget(int x)
{
    if (x/m!=bcj[x]/m)hp+=abs(bcj[x]/m-x/m)*v;
	if (bcj[x]==x)return;
	else fget(bcj[x]);//這裡是重現找的部分,只比標準的找多一行家HP
}
int get(int x)
{
	o++;
	if (o>n*m) {o=0;return 2147483647;}//若進入了死迴圈,跳出
	if (bcj[x]==x){o=0;return x;}
	else return get(bcj[x]);	//並查集標準找的部分,注意沒有路徑壓縮哦
}
void merge(int x,int y)
{
	bcj[x]=y;//不要連線到Y節點的樹根部分要連線到最下面
}
int main()
{
	int i,j,x,y,ans=2147483647,k;
	cin>>n>>m>>v>>x>>y;
	for (i=1;i<=n*m;i++)bcj[i]=i;
	for (i=1;i<=n;i++)
	 for (j=1;j<=m;j++)
	 cin>>b[i][j][1]>>b[i][j][2];//每層使用者的提供資訊
	for (i=1;i<=n;i++)
	  for (j=1;j<=m;j++)
	  {
		  merge((i-1)*m+j,(b[i][j][1]-1)*m+b[i][j][2]);//合併部分,(i-1)*m+j為當前使用者所在的i層第j間房,後面同理,不過是指向當前房間指向位置
	  }
	for (i=1;i<=m;i++)
	{
		if (get(i)==(x-1)*m+y)
		{fget(i);if (hp<ans)ans=hp;hp=0;}//若根節點為同學家,找,替換最小值
	}
	if (ans==2147483647)cout<<"impossible";
	else cout<<ans;//輸出
	return 0;
}

撒花結束