P10179 水影若深蓝 题解

· · 题解

P10179 水影若深蓝 题解

一道非常不错的思维题!

题目分析

题目给出的若干组点,每一组点之间的简单路径必须刚好经过两条边,也就是说,在这条简单路径上,会经过另外一个点。

可以将给出的每一组点看作拥有同一个父节点的节点。

所以,将每一组点进行连边,属于同一个连通块的节点放在同一层。对于每一个连通块,在下一个连通块中任选一个点,与当前连通块的全部点连边。对于最后一个连通块,即图中的红色节点,除去橙色节点已经选取的节点,其余节点向橙色的连通块的任意一个点连边,如图所示:

代码实现

用前向星存图,比较方便快捷。

通过 `bfs` 获取每一个连通块的点。 ```cpp #include <bits/stdc++.h> using namespace std; const int N=300005; const int M=600005; int T,n,m; int e[M],ne[M],h[N],tot; bool vis[N]; vector<int> v[N]; // 储存每一个连通块里的点 int cnt; // 连通块的数量 void add(int a,int b) { e[tot]=b,ne[tot]=h[a],h[a]=tot++; } void bfs(int s) { // find connected blocks queue<int> q; vis[s]=true; q.push(s); cnt++; v[cnt].push_back(s); while (!q.empty()) { int u=q.front(); q.pop(); for (int i=h[u];~i;i=ne[i]) { if (!vis[e[i]]) { vis[e[i]]=true; q.push(e[i]); v[cnt].push_back(e[i]); } } } } int main() { scanf("%d",&T); while (T--) { scanf("%d %d",&n,&m); for (int i=1;i<=cnt;++i) v[i].clear(); cnt=0; for (int i=1;i<=n;++i) h[i]=-1,vis[i]=0; while (m--) { int a,b; scanf("%d %d",&a,&b); add(a,b); add(b,a); // 无向图加双向边 } for (int i=1;i<=n;++i) { if (!vis[i]) { bfs(i); // 没有查找过,就 bfs 一下 } } if (cnt==1) { // 只有一个连通块,显然无解 puts("No"); continue; } puts("Yes"); // 否则有解 for (int i=1;i<=cnt;++i) { // 枚举每一个连通块 if (i!=cnt) { // 如果不是最后一个连通块 for (int j=0;j<v[i].size();++j) { // 向下一个连通块的第一个点连边 printf("%d %d\n",v[i+1][0],v[i][j]); } } else { // 最后一个连通块 for (int j=1;j<v[i].size();++j) { // 从第二个节点开始枚举,因为第一个已经被上一个连通块取走了 printf("%d %d\n",v[i][j],v[i-1][0]); } } } } return 0; } ``` 祝贺我赛时一遍 AC 此题!