环图应用--数组补全
数组补全
给定一个 \(1-n\) 的排列 \(f_1,f_2,f_3...f_n\)。
已知,对于 \(1 \leq n,f_i \neq i\) 始终成立。
现在,因为一些原因,数组中部分元素丢失了。
请你将数组丢失的部分补全,要求数组在补全后仍然是一个 \(1-n\) 的排列,且对于 \(1 \leq n,f_i \neq i\) 均成立。
对每个未丢失的元素,由 \(i\) 向 \(f_i\) 连接一条有向边,很容易得出原图应该是一个环图,每个结点都在环中,且入读 = 出度 = 1。
记录每个元素的出边和入边,如果无孤立点,则把环首尾连接,否则把孤立点加入任何一个环中即可。
特别的,如果图中全部都是孤立点或者环都是闭环,则孤立点单独形成一个环。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int main ()
{
int T; cin >> T;
while (T --)
{
int n; cin >> n;
vector<int> p(n+1, 0), q(n+1, 0); // p表示出边,q表示入边
vector<bool> st(n+1, 0); // 是否被考虑过
for (int i = 1; i <= n; i++)
{
cin >> p[i];
q[p[i]] = i;
}
bool f = false; // 记录孤立点是否被加入
for (int i = 1; i <= n; i++)
{
if (st[i] || !q[i]) // 以及被考虑过或者是孤立边
continue;
st[i] = true;
int x = i, y = i; // x是环起点,y是终点
while (q[x] && !st[q[x]])
{
st[q[x]] = true;
x = q[x];
}
while (p[y] && !st[p[y]])
{
st[p[y]] = true;
y = p[y];
}
if (q[x] == y) continue;
if (!f)
{
f = !f;
for (int i = 1; i <= n; i++)
{
if (p[i] || q[i]) continue;
st[i] = true;
p[y] = i;
y = i;
}
}
p[y] = x;
}
// 图上都是孤立点或者所有的环都是闭环,孤立点构造环
if (!f)
{
int x = 0, y = 0;
for (int i = 1; i <= n; i++)
{
if (p[i] || q[i]) continue;
if (!x && !y) x = y = i;
else
{
p[y] = i;
y = i;
}
}
p[y] = x;
}
for (int i = 1; i <= n; i++)
cout << p[i] << ' ';
cout << endl;
}
}