- p2661 信息传递
这道题实际上就是求图的最小环,这里用并查集的方法实现。
#include <cstdio>
#include <algorithm>
using namespace std ;
const int N = 2e5 + 5 ;
int s[N] , h[N] ;
int ans = N ;
int find(int x){
if (x != s[x]){
int t = s[x] ; //记录父节点
s[x] = find(s[x]) ; //更新为祖先节点
h[x] += h[t] ;
//h[x]是x相对于父节点的长度,h[t]是x的父节点相对于祖先节点的长度,合起来就是x相对于祖先节点的长度,且x已经更新为祖先节点了
}
return s[x] ;
}
void built(int x , int y){
int a = find(x) ;
int b = find(y) ;
if (a != b){
s[a] = b ;
h[x] = h[y] + 1 ;
}else{
ans = min(ans,h[x]+h[y]+1) ; //祖先节点相同则可以成环(环的长度为两个节点到祖先节点的长度+1)
}
}
int main(){
int n ;
scanf ("%d",&n) ;
for (int i = 1 ; i <= n ; ++ i){
s[i] = i ;
h[i] = 0 ;
}
for (int i = 1 ; i <= n ; ++ i){
int x ;
scanf ("%d",&x) ;
built(i,x) ;
}
printf ("%d\n",ans) ;
return 0 ;
}
- p1330 封锁阳光
题意是每个点要分隔开但是取走的点不能相邻,转化为染色就是相邻的点不能为同种颜色。
做法:遍历未染色的点,将未染色的点染为黑色,相邻的点染为白色,依次类推,要注意每次dfs是当前的联通图,取断点要去当前的最小值,所以在开始遍历那里之前,颜色数量要清0。
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std ;
const int N = 1e4 + 5 ;
int n , m ;
int vis[N] , color[2] ;
vector<int> r[N] ;
int ans ;
int dfs(int i){
for (int j = 0 ; j < r[i].size() ; ++ j){
int t = r[i][j] ;
if (vis[t] == -1){
vis[t] = (vis[i]+1)%2 ;
color[vis[t]] ++ ;
dfs(t) ;
}
else{
if (vis[i] == vis[t]){ //新的颜色和旧的颜色相同(冲突)
printf ("Impossible\n") ;
exit(0) ; //退出程序
}
}
}
return min(color[0],color[1]+1) ;
}
int main(){
scanf ("%d%d",&n,&m) ;
memset(vis,-1,sizeof(vis)) ;
while(m--){ //建图
int v , u ;
scanf ("%d%d",&v,&u) ;
r[v].push_back(u) ;
r[u].push_back(v) ;
}
for (int i = 1 ; i <= n ; ++ i){
if (r[i].size() == 0) continue ; //为单独的点不用搜索
color[0] = 0 , color[1] = 0 ; //注意清0,因为每次要返回当前连通图染色数最少的点
if (vis[i] == -1){ //未上色
vis[i] = 1 ;
ans += dfs(i) ;
}
}
printf ("%d\n",ans) ;
return 0 ;
}