最小移动距离

平面上有 n 个点,编号为 1∼n。

对于每个点 i(1≤i≤n),都存在一条从点 i 到点 ai(1≤ai≤n,ai 可以等于 i)的有向边。

所有边的长度均为 1。

请你判断是否存在一个最小移动距离 t(t≥1),使得:

我们规定,如果从点 u 出发,移动 t 单位长度距离后,到达点 v,就称点 v 是点 u 的目标点。注意,一个点的目标点也可能是它自己。
对于图中的每个点 x,如果点 y 是点 x 的目标点,则点 x 也必须是点 y 的目标点。
如果存在这样的 t,请你输出 t 的最小可能值,否则请你输出 -1。

输入格式
第一行包含一个整数 n。

第二行包含 n 个整数 a1,a2,…,an。

输出格式
如果存在满足条件的 t(t≥1),则输出一个正整数,表示 t 的最小可能值。

否则输出 -1。

数据范围
前 3 个测试点满足 1≤n≤4。
所有测试点满足 1≤n≤100,1≤ai≤n。

输入样例1:
4
2 3 1 4
输出样例1:
3
输入样例2:
4
4 4 4 4
输出样例2:
-1
输入样例3:
4
2 1 4 3
输出样例3:
1

基环树,又称环套树,最显著的特点就是有 N个点 N 条边,诶,比一棵树多一条边,这就导致这个图上出现了一个唯一的环,当然,这是保证这 N 个点 N 条边构成的是一个连通图的时候才是唯一环,如果图不连通但是每个连通块点数都等于边数的时候这个图就是一个基环树森林,可以有几个环。

该题中有n个点n条边,所以是一个基环树的形式,如果环外挂有树的话,从树外一个叶节点出发它的入度为零,永远无法回到自己,故该题中是由多个环构成。

对于奇数长的环来说,任一点的目标点只能是它自己,它最少移动环的长度,偶数长的环来说,任一点的目标点是与其距离(换的长度)/2的点,他最少移动环的长度/2就可以到达其目标点,题目所求的就是所有环的长度最小公倍数(偶数长的环要除以2)

判环要判断当前所有的点的入度都为1,因为该题中所有点的出度都为一。

#include<iostream>
using namespace std;
typedef long long LL;
const int N=110;
 int n;
int s[N],I[N],p[N];
int gcd(int a, int b)  // 欧几里得算法
{
    return b ? gcd(b, a % b) : a;
}
int find(int x)  // 并查集
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}

LL work(){
    for(int i=1;i<=n;++i)if(I[i]!=1)return -1;
    LL res=1;
    for(int i=1;i<=n;++i){
        if(p[i]==i){
            int len =s[i];
            if(len%2==0)len/=2;
            res=res*(len/gcd(res,len));
        }
    }
    return res;
}
int main(){
  
   cin>>n;
   for(int i=1;i<=n;++i)p[i]=i,s[i]=1;
   for(int i=1;i<=n;++i){
       int x;
       cin>>x;
       I[x]++;
       int a=find(i),b=find(x);
       if(a!=b){
           s[b]+=s[a];
       p[a]=b;
       }
       
   }
   cout<<work()<<endl;
   return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值