校赛Round1 1009 死胡同

Problem Description

  一个死胡同由排成一列的 n 个格子组成,编号从 1 到 n 。
  实验室的“猪猪”一开始在1号格子,开始向前走,每步一格,并且每走 k 步会在当前的格子上打上记号(开始时,1号格子也有记号)。由于这是死胡同,每当“猪猪”走到最左或者最右的格子时,它会改变方向。好奇的“猪猪”在想:如果我一直走,能否把所有格子都打上记号呢?
  聪明的你一定知道答案!

  Hint1:如果 n=6,k=2,位置变化为:1 -> 3 -> 5 -> 5 -> 3 -> 1 -> 3 -> 5 .... 显然,此时不能将所有格子打上标记。(如下图)
  

Input

  多组输入数据(组数<=100)
  每组数据一行,包含两个正整数 n 和 k。
  (1 <= n <= 100000 , 1 <= k <= 100000)

Output

  对于每组数据输出一行 YES 或者 NO 代表能否给所有格子打上标记。

Sample Input

6 2
6 3

Sample Output

NO
YES

Author

Natureal


声明:由于再次提交时已经Out of Contest Time,所以自己的做法无法百分百保证正确。但是我和学长的答案算法随机测了10组相同的数据组,结果一致,应该八九不离十了吧~【机智的我,从学长那要来了后台测试数据,答案完全匹配~2015/12/14/13:35】

/*
首先将走格子写成一排,即:
123...(n-1)n(n-1)(n-2)...32123...(n-1)n(n-1)(n-2)...,可见循环内容是
123...(n-1)n(n-1)(n-2)...32
周期是c=2*n-2,这样判断的目的是当m太大时,可以将m%=c,从而减少时间损耗
判断YES/NO的条件是:
当猪猪顺某个方向走时正好走到已经走过的格子,判断这时是否已经走完全部格子。
*/
#include <stdio.h>
#include <string.h>
#define maxn 100010
int a[maxn];//a[maxn]=1:该格子已被踩过;a[maxn]=0:该格子还未被踩
int main()
{
	//freopen("in.txt", "r", stdin);
	//freopen("out.txt", "w", stdout);
	int n, k, i, sum;
	while (~scanf("%d%d", &n, &k))
	{
		if (n == 1)
		{
			printf("YES\n");
			continue;
		}
		memset(a, 0, sizeof(a));
		k %= (2 * n - 2);
		i = 1;
		sum = 0;
		while(1)
		{
			if (i <= n)
			{//统一在方向1->n时踩到已踩过格子时退出
				if (a[i]) break;
				else a[i] = 1;
			}
			else a[2 * n - i] = 1;
			i += k;
			if (i > 2 * n - 2)
				i -= (2 * n - 2);
		}
		for (i = 1;i <= n;i++)
			sum += a[i];
		if (sum == n) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}


下面贴出来学长的做法,膜拜!

思路:【首先特判掉 n =1 的情况,此时必定是YES,接下来考虑 n > 1 的情况。】

  考虑猪猪的行走路线,如果 n=4,那么遍历到的格子:12343212343212...,以 123432 为一个周期,其长度为 2n-2 。

  做法1:由于 n <= 100000,这道题可以暴力模拟来做,不断地走直到某个位置某一朝向的情况出现两次,再推出循环。但是要考虑到 k 远大于 n 的情况下性能较差的情况,所以先将 k %= (2n-2),这样就可以通过。

  做法2:由于 2n-2 是一个周期,那么可以把问题看成猪猪在一个长为 2n-2 的环里面无限地走,每次走 k 步,可以证明如果 GCD(2n-2,k)=1,可以走到所有格子。

  简证:【用 GCD 代表 2n-2 和 k 的最大公约数,用 LCM 代表两者的最小公倍数】从 1 出发,不断地每次走 k 步,直到再次走到 1 形成一个周期,此时走的步数必定为 p*(2n-2),p为一个>=1的常数,易得 p*(2n-2)= LCM 那么在第二次走到1之前,总共标记了m = LCM / k 个格子,由于 LCM=(2n-2)* k / GCD,那么 m = (2n-2)/ GCD,如果 GCD>=2,那么 m < n。由上述,每个周期起点都为1,且标记的点相同,点数都为 m < n,所以 GCD >=2 时不能标记所有点,答案为NO。

  接下来证明 GCD = 1 时能标记所有点,考虑另外一个问题:一个有 L 个格子的环,从 1 出发,每次跳 k 个格子能否跳到所有格子。显然重复跳到某个格子至少需要跳 LCM(L,k) 个格子(设为结论1),跳了 LCM / k 次,如果 GCD(L,K)=1,那么跳的次数为 L 次,这期间必定跳到 1~L 所有点,即证明:期间跳到的格子不重复。如果跳到的点重复,则说明重复跳到某个格子至少需要跳的格子数 < LCM,与结论1矛盾,得证。

//做法1
#include <stdio.h>
#include <string.h>

const int MAX = 102400;
const int LEFT = 0;
const int RIGHT = 1;

int N, K;
int pVisited[2][MAX];

int main() {
	while (scanf("%d%d", &N, &K) != EOF) {
		if (N == 1) {
			printf("YES\n");
			continue;
		}
		K %= 2 * (N - 1);
		memset(pVisited, false, sizeof(pVisited));
		int nDir = RIGHT, nCur = 1;
		while (!pVisited[nDir][nCur]) {
			pVisited[nDir][nCur] = true;
			if (nDir == RIGHT) {
				nCur += K;
				if (nCur > N) {
					nCur = 2 * N - nCur;
					nDir = LEFT;
				}
				if (nCur < 1) {
					nCur = 2 - nCur;
					nDir = RIGHT;
				}
			}
			else {
				nCur -= K;
				if (nCur < 1) {
					nCur = 2 - nCur;
					nDir = RIGHT;
				}
				if (nCur > N) {
					nCur = 2 * N - nCur;
					nDir = LEFT;
				}
			}
		}
		bool bFlag = true;
		for (int i = 1; i <= N; i++) {
			if (!pVisited[LEFT][i] && !pVisited[RIGHT][i])
			{
				bFlag = false; break;
			}
		}
		if (bFlag) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

//做法2
#include <stdio.h>

int n, k;

int Gcd(int a, int b) {
	return b == 0 ? a : Gcd(b, a % b);
}

int main() {
	while (scanf("%d%d", &n, &k) != EOF) {
		if (n == 1) {
			printf("YES\n");
			continue;
		}
		if (Gcd(2 * n - 2, k) == 1) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值