换位置游戏+洗牌

upc:

N 个小朋友(编号为 1 到 N)正在玩一个换位置游戏。从左到右依次排列着 N 个凳子 (编号为 1 到 N,最左边的为 1 号凳子,最右边的为 N 号凳子),每个凳子上都有一个数字 (凳脚处红色数字),每个数字互不相同,且都是不超过 N 的正整数。 
游戏开始前,1 号小朋友坐在 1 号凳子上,2 号小朋友坐在 2 号凳子上,然后依次下去, N 号小朋友坐在 N 号凳子上。比如当 N=4 时,游戏开始前小朋友们坐凳子的状态如下图 1 所示: 


图 1  游戏开始前 4 位小朋友坐凳子的状态 
坐定后,游戏开始。每位小朋友看一下自己坐的凳子凳脚处的数字,然后根据这个数字 找到相应号码的凳子。比如上面的例子,1 号小朋友凳脚处数字是 3,所以他到 3 号凳子上 坐下,2 号小朋友凳脚处数字是 1,所以他到 1 号凳子坐下,3 号小朋友凳脚处数字是 2,所 以他到 2 号凳子坐下,4 号小朋友凳脚处数字是 4,所以他到 4 号凳子坐下。经过一轮换位 置以后,4 个小朋友坐凳子的状态如下图 2 所示: 

图 2  经过第 1 轮换位置后小朋友们坐凳子的状态 
坐定后,每位小朋友再看一下自己凳脚的数字,按照凳脚的数字再继续换位置,第二轮 换位置的结果如下图 3 所示: 

图 3  经过第 2 轮换位置后小朋友们坐凳子的状态 
坐定后,每位小朋友再看一下自己凳脚的数字,按照凳脚的数字再继续换位置,第三轮 换位置的结果如下图 4 所示: 

图 4  经过第 3 轮换位置后小朋友们坐凳子的状态 
当第三轮换位置结束后,发现每位小朋友又各自坐到了游戏开始前的位置上,此时游戏结束。 
从上面的过程我们可以发现,从游戏开始经过 3 轮换位置后又回到了游戏开始前坐凳子 的状态,但当 N 很大的时候,这个换位置过程非常复杂,请编程帮忙计算一下最少需要经 过多少轮换位置才能回到游戏开始前坐凳子的状态。 

输入

输入共 2 行。 
第 1 行是一个整数 N(1≤N≤1000),表示参加游戏的小朋友人数,当然也表示凳子的 数目。 
第 2 行 N 个互不相同的正整数 Ki(1≤Ki≤N,且 1≤i≤N),Ki 表示第 i 把凳子凳脚处的数字。 

输出

输出共 1 行。 
表示小朋友们通过换位置后回到游戏开始前坐凳子的状态最少需要经过多少轮。测试数 
据保证输出的结果不超出 20000000。 

样例输入 Copy

3
1 2 3

样例输出 Copy

1

提示

输入有3把凳子。1号凳子凳脚处的数字为1,2号凳子凳脚处的数字为2,3号凳子凳脚处的数字为3。第1轮换位置后,1号小朋友仍然坐在1号凳子上,2号小朋友仍然坐在2号凳子上,3号小朋友仍然坐在3号凳子上。所以经过1轮就回到了游戏开始前状态。
对于60%的数据,1≤N≤500,且最少需要交换的轮数不超过10000。
对于100%的数据,1≤N≤1000,且最少需要交换的轮数不超过20000000。

 

#include <cstdio>
#define MX 1e9
#define MN -1e9
typedef long long int ll;
using namespace std;
ll gcd(ll n,ll m){
    return m?gcd(m,n%m):n;       
}
ll lcm(ll n,ll m){
    return n/gcd(n,m)*m;
}
int pos[1010],n,i;                //pos[i]储存的是第i把凳子凳脚处的数字
ll swp(int st,int no,ll cnt){     //cnt用来储存这位小朋友交换座位的次数
    if(no==st)                    //st储存的是这位小朋友的初始位置,no则是他的现在位置。
        return cnt;
    else
        swp(st,pos[no],cnt+1);
}
int main() {
    ll ans=1;
    scanf("%lld",&n);
    for(i=1;i<=n;i++)
        scanf("%d",&pos[i]);
    for(i=1;i<=n;i++)
        ans=lcm(ans,swp(i,pos[i],1));
    printf("%lld",ans);
}

上海计算机协会: 

洗牌

内存限制: 256 Mb时间限制: 1000 ms

题目描述

给定一个整数 nn,表示 nn 张牌,牌的编号为 11 到 nn。

再给定一个洗牌置换 f_1,f_2,\dots,f_nf1​,f2​,…,fn​,进行一次洗牌操作时,应将第一号位置的牌交换到第 f_1f1​ 号位置,将第 ii 号位置的牌交换到第 f_ifi​ 号位置。保证 ff 是一个 11 到 nn 的排列(即 11 到 nn 中的每个数字出现且只出现一次)。

一开始,牌的顺序为 1,2,\cdots,n1,2,⋯,n。给定一个整数 kk,请输出经过 kk 次洗牌后牌的顺序。

输入格式

第一行:两个整数 nn 与 kk;
第二行:nn 个整数表示 f_1,f_2,\dots,f_nf1​,f2​,…,fn​。

输出格式

单独一行:nn 个整数表示洗 kk 次牌后的顺序。

数据范围

  • 对于 30\%30% 的数据,1\leq n\leq 1001≤n≤100,1\leq k\leq 10001≤k≤1000;
  • 对于 60\%60% 的数据,1\leq n\leq 10001≤n≤1000,1\leq k\leq 10,0001≤k≤10,000;
  • 对于 100\%100% 的数据,1\leq n\leq 100,0001≤n≤100,000,1\leq k\leq 1,000,000,0001≤k≤1,000,000,000。

样例数据

输入:

输出:

说明:

输入:

输出:

说明:

60分代码(原100分)感觉也不是很复杂,但终归错了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
ll a[maxn],c[maxn];
ll gcd(ll n,ll m)
{
    return m?gcd(m,n%m):n;
}
ll lcm(ll n,ll m)
{
    return n/gcd(n,m)*m;
}
ll pos[maxn],n,i,t,j;                //pos[i]储存的是第i把凳子凳脚处的数字
ll swp(int st,int no,ll cnt)      //cnt用来储存这位小朋友交换座位的次数
{
    if(no==st)                    //st储存的是这位小朋友的初始位置,no则是他的现在位置。
        return cnt;
    else
        swp(st,pos[no],cnt+1);
}
int main()
{
    ll ans=1;
    scanf("%lld%lld",&n,&t);
    for(i=1; i<=n; i++)
    {
        scanf("%lld",&pos[i]);
    }
    for(i=1; i<=n; i++)
        ans=lcm(ans,swp(i,pos[i],1));

    t=t%ans;
    if(t==0)
    {
        for(i=1; i<=n; i++)
        {
            printf("%lld ",i);

        }
        return 0;
    }
    for(i=1; i<=n; i++)
    {
        ll m=i;
        for(j=1; j<=t; j++)
        {
            m=pos[m];

        }
        c[m]=i;
    }
    for(i=1; i<=n; i++)
    {
        cout<<c[i]<<" ";
    }


    return 0;
}

 100分代码:

#include<bits/stdc++.h>
using namespace std;
int n,k,a[100005],f[100005][30],b[100005];//a[i]为洗牌规则,b数组存储洗牌后的数字,
//f[i][j]为节点i向上2的j次方的节点是谁,初始化f[i][0]就是节点i向上2的0次方的节点也就是a[i] 
void st()
{
	//外层必须先枚举j,否则例如求f[1][2]会转移到f[f[1][1]][1],而此时没有该值,导致错误 
	for(int j=1;(1<<j)<=k;j++)//保证2的j次方小于等于k,不能洗超过k的次数 
	{
		for(int i=1;i<=n;i++)//枚举每一个起点 
		{
			f[i][j]=f[f[i][j-1]][j-1];//节点i向上2的j次方转移到节点i移动2的j-1次方后的那个点再移动2的j-1次方。 
		}
	}
}
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		f[i][0]=a[i];//初始化 
	}
	st();
	for(int i=1;i<=n;i++)
	{
		//快速幂 
		int y=k;
		int ans=0;
		int x=i;
		while(y) 
		{
			if(y&1)
			{
				x=f[x][ans];
			}
			ans++;
			y>>=1;
		}
		b[x]=i;//赋值 
	}
	for(int i=1;i<=n;i++) cout<<b[i]<<" ";
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值