AcWing 4626. 最小移动距离

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

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

所有边的长度均为 11。

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

  • 我们规定,如果从点 uu 出发,移动 tt 单位长度距离后,到达点 vv,就称点 vv 是点 uu 的目标点。注意,一个点的目标点也可能是它自己。
  • 对于图中的每个点 xx,如果点 yy 是点 xx 的目标点,则点 xx 也必须是点 yy 的目标点。

如果存在这样的 tt,请你输出 tt 的最小可能值,否则请你输出 -1

输入格式

第一行包含一个整数 nn。

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

输出格式

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

否则输出 -1

数据范围

前 33 个测试点满足 1≤n≤41≤n≤4。
所有测试点满足 1≤n≤1001≤n≤100,1≤ai≤n1≤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

题意分析:判断一个点走t步走到另外一个点,然后这个另外的点再走t步走到原先的点(可以是本身),也就是给定n个点,n个点能够分别组成环,并且求出一个步数t,表示每个环走t步都能回到起点位置:找到这几个环的长度的最小公倍数即可 

(注意:如果环的长度是偶数1->2->3->4那么长度可以减半:1的终点没必要还是1,变成3即可)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=110;
int n;
int a[N];
bool vis[N];
ll gcd(ll x,ll y) { //求最小公因数
	if(x<y)return gcd(y,x);
	if(!y) return x;//y是0
	return gcd(y,x%y);
}
int main() {
	cin>>n;
	for(int i=1; i<=n; i++) {
		cin>>a[i];
	}
	ll ans=1;//初始化最大公倍数
	for(int i=1; i<=n; i++) {
		memset(vis,false,sizeof(vis));
		int j=a[i];
		ll res=1;
		vis[i]=true;
		while(!vis[j]) {
			vis[j]=true;
			j=a[j];//j变为下一个可行点
			res++;//路径长度++
		}
		if(j!=i) { //走不到自身
			ans=-1;
			break;
		}
		/*偶数,那么环的大小要减半
		比如1->2->3->4->1,
		那么1的目标点就可以是3,
		而不非要是自己
		所以路径大小减半。
		*/
		if(res%2==0) { //路径是偶数
			res/=2;
		}
		if(ans==-1) {
			cout<<-1;
			return 0;
		}
		ans=ans/gcd(ans,res)*res;//最大公倍数*最小公因数==a*b
	}
	cout<<ans;

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值