HDU ACM Steps 1.2.2 hide handkerchief 详解


一、问题描述

hide handkerchief

The Children’s Day has passed for some days .Has you remembered something happened at your childhood? I remembered I often played a game called hide handkerchief with my friends.
Now I introduce the game to you. Suppose there are N people played the game ,who sit on the ground forming a circle ,everyone owns a box behind them .Also there is a beautiful handkerchief hid in a box which is one of the boxes .
Then Haha(a friend of mine) is called to find the handkerchief. But he has a strange habit. Each time he will search the next box which is separated by M-1 boxes from the current box. For example, there are three boxes named A,B,C, and now Haha is at place of A. now he decide the M if equal to 2, so he will search A first, then he will search the C box, for C is separated by 2-1 = 1 box B from the current box A . Then he will search the box B ,then he will search the box A.
So after three times he establishes that he can find the beautiful handkerchief. Now I will give you N and M, can you tell me that Haha is able to find the handkerchief or not. If he can, you should tell me "YES", else tell me "POOR Haha".

二、题解

1.转化为最大公约数

如果不仔细思考,可能会将此题当作一个模拟题来处理,过程也不复杂,按照规则全部扫描一遍再判断即可,效率不高,时间复杂度为 Θ ( N ) \Theta(N) Θ(N)
空间复杂度 Θ ( N ) \Theta(N) Θ(N)


下面我们进一步思考
   假设出发的位置编号为x,那么下一步的编号为(x+M)%N,k步后的位置编号为(x+KM)%N。并且我们每走一步,就将对应的位置进行标记一次(对应题目中的检查box一次)。
  那么我们来考虑首次出现某个位置(记为key point),被标记了两次时的情况,显然,当回到key point时,后面再走下去也只是在之前已经走过的路线上不断循环,不会再有新的位置被标记。所以,如果回到key point时的步数小于N,由于每一步只会标记一个点,那么我们可以肯定,一定存在没有被标记的点(对应题目中未被检查的box),且以后也不可能会被检查到,所以这种情况一定是失败的。并且, 由于每走一步都一定会标记一个点(检查一个box),所以第一次出现key point(即第一次对某个box进行重复检查)的步数一定小于box的总数N。
  因此,成功找到handkerchief时首次出现key point走过的步数一定恰好是N,此时,根据之前的位置公式,将k = N代入,可以发现,N步后,一定是恰好回到了出发的位置x,即 (x+NM) % N = x,因此,我们可以将原问题转换为如下问题:
给定N,M,判断 M,N是否满足:kM % N = 0 对于所有的 1≤k<N都不成立。
显然,这也就是说,M,N的最小公倍数是MN,再换而言之,也就是说,M,N的最大公约数为1,这样,我们很容易想到,用辗转相除法求出两者的最大公约数,然后判断是否为1即可,关于辗转相除法的高效设计,这里有一篇文章写的很好,大家可以补充看看。
辗转相除法

2.参考代码

代码如下(示例):

#include<iostream>
#include<cstdio>
using namespace std;

int gcd(int a,int b)
{
    if(a == 1 || b == 1)
        return 1;
    if(a == b)
        return a;
    if(a < b)
        return gcd(b,a);
    else {
        if(!a & 1 && !b & 1)
            return gcd(a >> 1, b >> 1) << 1;
        else if(!a & 1 && b & 1)
            return gcd(a >> 1,b);
        else if(a & 1 && !b & 1)
            return gcd(a,b >> 1);
        else
            return gcd(b,a - b);
    }

}

int main()
{
    int N = 0, M = 0;
    while(scanf("%d%d",&N,&M) && N != -1) {
        if(gcd(N,M) == 1)
            printf("YES\n");
        else
            printf("POOR Haha\n");
    }
}



这里使用文章中的辗转相除代码,结果速度并不是很快,说明测试数据中小数多一些,如果想让结果更快些,可以直接使用一般的辗转相除法。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值