H:洗牌(选作)
成绩 | 5 | 开启时间 | 2022年11月21日 星期一 08:00 |
折扣 | 0.8 | 折扣时间 | 2022年12月31日 星期六 23:55 |
允许迟交 | 否 | 关闭时间 | 2022年12月31日 星期六 23:55 |
假设我们有 2n 张牌,它们以 1, 2, ..., n, n+1, ..., 2n 编号并在开始时保持着这种顺序。一次洗牌就是将牌原来的次序变为 n+1, 1, n+2, 2, ..., 2n, n,也就是将原来的前 n 张牌放到位置 2, 4, ..., 2n,并且将余下的 n 张牌按照他们原来的次序放到奇数位置 1, 3, ..., 2n-1。已经证明对于任何一个自然数 n,这 2n 张牌经过一定次数的洗牌就回到原来的次序。但我们不知道对于一个特定的 n,需要几次洗牌才能将牌洗回原来的次序。
输入:
牌张数的一半n,即初始情况下一共有2n张牌,n为int型整数
输出:
将牌洗回原来的次序所需要的洗牌次数
第一种方法:将所有牌视作一个数组,将牌数初始化为牌的位置,对所有牌进行变换直到复原。
int main(void)
{
int n, cnt = 0;
scanf("%d", &n);
int *brand1, *brand2;
brand1 = (int*)malloc((2 * n + 1) * sizeof(int));
brand2 = (int*)malloc((2 * n + 1) * sizeof(int));
/*动态数组*/
for (int i = 1; i <= 2 * n; i++)
brand1[i] = i;/*初始化*/
do{
cnt++;
for (int i = 1; i <= n; i++)/*一次洗牌*/
{
brand2[2 * i] = brand1[i];
brand2[2 * i - 1] = brand1[n + i];
}
memcpy(brand1, brand2, (2 * n + 1) * sizeof(int));
/*滚动数组思想*/
} while (brand1[1] != 1);
free(brand1);
free(brand2);
printf("%d\n", cnt);
return 0;
}
*注:动态数组在数组大小受变量影响时(尤其是这题的情况,数组大小可以很小,也可以很大)很好用,否则至少要写成brand[20001],很占用空间。
第二种方法:只考虑一张牌的位置,只要最后这一张牌回到原位即可。显然这种方法运行更快,占用内存少得多。
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d",&n);
int count = 0;
for (int i = 1; i <= 2*n; count++)
{
if (i < n + 1)
i *= 2;
else
i=(i - n)*2-1;
if (i == 1)
break;
}
printf("%d\n", ++count);
return 0;
}
本人C语言菜鸟一枚,代码不当或可以改进的地方欢迎大家讨论交流。