題目描述
曹是一隻愛刷街的老曹,暑假期間,他每天都歡快地在陽光大學的校園裏刷街。河蟹看到歡快的曹,感到不爽。河蟹決定封鎖陽光大學,不讓曹刷街。
陽光大學的校園是一張由N個點構成的無向圖,N個點之間由M條道路連接。每隻河蟹可以對一個點進行封鎖,當某個點被封鎖後,與這個點相連的道路就被封鎖了,曹就無法在與這些道路上刷街了。非常悲劇的一點是,河蟹是一種不和諧的生物,當兩隻河蟹封鎖了相鄰的兩個點時,他們會發生衝突。
詢問:最少需要多少隻河蟹,可以封鎖所有道路並且不發生衝突。
輸入格式
第一行:兩個整數N,M
接下來M行:每行兩個整數A,B,表示點A到點B之間有道路相連。
輸出格式
僅一行:如果河蟹無法封鎖所有道路,則輸出“Impossible”,否則輸出一個整數,表示最少需要多少隻河蟹。
輸入輸出樣例
輸入 #1複製
3 3 1 2 1 3 2 3
輸出 #1複製
Impossible
輸入 #2複製
3 2 1 2 2 3
輸出 #2複製
1
說明/提示
【數據規模】
1<=N<=10000,1<=M<=100000,任意兩點之間最多有一條道路。
思路
有一個由n個結點構成的有m條邊的圖,需要在某幾個點上放置河蟹,河蟹會佔領該點上的所有邊,相鄰的河蟹之間會發生衝突,目的要讓河蟹佔領整個圖。
我用的並查集,具體做法是把一張圖分層,有點類似於bfs。一個合理的圖不會出現同一層之間互相連接的邊。這種圖叫做分層圖。
如果分層後,同一層兩點之間有邊相連,就可以判定不行。
河蟹直接不能相鄰,那麼我們只要讓河蟹佔據奇數層或者偶數層的所有點,不就ok?那麼我們可以用並查集維護每一層。並查集的作用是使某些點具有相同的祖宗結點,那麼他們直接就屬於一個集合。同理,讓奇數層的點屬於同一個集合,偶數點屬於同一集合,有點類似於關押罪犯。
#include <stdio.h>
#include <iostream>
#include <memory.h>
using namespace std;
int n,m,s1,s2,fa[100001]/*每個節點的祖宗是誰*/,e[100001][2]/*記錄圖信息*/
bool vis[100001];
int getfind(int v)//認爹函數
{
if(fa[v]==v)//祖宗節點是自己
{
return v;
}
else//繼續認爹
{
fa[v]=getfind(fa[v]);
return fa[v];
}
}
inline void merge(int v,int u)//合併
{
register int t1,t2;
t1=getfind(v);//找他們的祖宗節點
t2=getfind(u);
if(t1!=t2)//不相同,就合併集合
{
fa[t2]=t1;
}
return;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
register int i,j;
cin>>n>>m;
for(i=1;i<=n*2;i++)
{
fa[i]=i;
}
for(i=1;i<=m;i++)
{
cin>>e[i][0]>>e[i][1];
int t1(getfind(e[i][0])),t2(getfind(e[i][1]));//先找祖先
if(t1==t2)//祖先一樣,且他們在同一層
{
cout<<"Impossible"<<endl;
return 0;
}
else
{
merge(e[i][0]+n,e[i][1]);
merge(e[i][0],e[i][1]+n);
}
}
memset(vis,1,sizeof(vis));//以i爲根節點的圖是否放置和諧
int s(0);
for(i=1;i<=m;i++)
{
int t1(getfind(e[i][0])),t2(getfind(e[i][1]));
if(vis[t1]==1 && vis[t2]==1)//如果沒有
{
vis[t1]=0;
vis[t2]=0;
s1=s2=0;
for(j=1;j<=n;j++)//在改分層圖進行遍歷找最佳答案
{
if(getfind(j)==t1) s1++;
else if(getfind(j)==t2) s2++;
}
s+=min(s1,s2);
}
}
cout<<s<<endl;
return 0;
}