本题中需要找某个编号下的值是否是另一个的编号,如果可以凑出值和编号的两者对应,那么就证明可以。
由于有对应关系出现,所以可以将某个点根据值和另一个点的编号向关联起来,连上一条线。这样形成了一个图。如果有入度为0的节点的存在那么就必须将其留下,因为没有其他的节点的值可以对应上它的编号,所以如果将他变到圈起来的那一部分不可能成功的。再由如果当前的节点需要留下的话,那么他所管理的下一个节点就必须被圈走,否则它的值没有对应。那么在这里就可以判断是否可行。
还有就行图中可能存在回路,在这里我们随便让其中一个变成0入度,然后重复上面的过程就行。
需要注意的一点是关于代码中为什么要在solve()中已经将当前和接下来的节点状态改变之后再去判断下下个节点是否需要入队(if(--in[a[a[i]]]==0)q.push(a[a[i]]);):一方面多去判断一下就可以将这个节点及其下面这个区域图里面的节点给走完,更重要的是如果出现题目中第二个测试点这种自回路的情况可以去判断其不符合。
这是我做过第一道将题目中信息转化成图论去解的题,挺有意思的。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
//在vis里面1表示留下,-1表示被圈走,0表示还未处理。
int n, a[maxn], in[maxn], vis[maxn], flag;
queue<int> q;
void solve() {
while (q.size()) {
int i = q.front();q.pop();
if (vis[i]==-1||vis[a[i]]==1) flag = 1;
vis[i] = 1;//因为没有入队,所以直接留下
if (vis[a[i]]==-1) continue;
vis[a[i]] = -1;//相对于的那个地方就得被圈走,不然无法对应相等。
if(--in[a[a[i]]]==0)q.push(a[a[i]]);
}
}
int main() {
cin>>n;
for (int i=1;i<=n;i++) {
cin>>a[i];in[a[i]]++;//统计入度数量。
}
while (1) {
for (int i=1;i<=n;i++) {
if (in[i]==0&&vis[i]==0) {
q.push(i);
}
}
if (q.size()==0) {
for (int i=1;i<=n;i++) {
if (vis[i]==0) {
q.push(i);
solve();
}
}
break;
} else {
solve();
}
}
if (flag) {
cout<<-1;
return 0;
}
vector<int> st;
for (int i=1;i<=n;i++) {
if (vis[i]==1) {
st.push_back(a[i]);
}
}
cout<<st.size()<<endl;
for (int i=0;i<st.size();i++) {
cout<<st[i]<<" ";
}
return 0;
}