详解关于硬币翻转的两个问题

目录

一、P1146 硬币翻转

二、P2708 硬币翻转

代码实现一:

代码实现二:



一、P1146 硬币翻转

题目描述:在桌面上有一排硬币,共 N(N 为不大于 100 的偶数) 枚,每一枚硬币均正面朝上。现在要把所有的硬币翻转成反面朝上,规则是每次可翻转任意 N - 1 枚硬币(正面向上的被翻转为反面向上,反之亦然)。求一个最短的操作序列(将每次翻转 N - 1 枚硬币称为一次操作)。

题目链接:P1146 硬币翻转 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

代码实现:

#include <stdio.h>

#define MAX 100

int main()
{
	int arr[MAX] = { 0 };  // 0 表示正面朝上,1 表示反面朝上
	int N = 0;
	scanf("%d", &N);
	printf("%d\n", N);
	int i = 0;
	int j = 0;
	for (i = 0; i < N; i++)  // 至少进行 N 次操作(每次操作即翻转 N - 1 枚硬币)
	{
		for (j = 0; j < N; j++)  // 第 i 次操作即翻转除了第 i 枚硬币以外的所有硬币
		{
			if (j != i)
			{
				if (arr[j] == 0) 
				{
					arr[j] = 1;
				}
				else if(arr[j] == 1)
				{
					arr[j] = 0;
				}
			}
			printf("%d", arr[j]);
		}
		printf("\n");
	}
	return 0;
}
  • 思路

    翻转任意 N - 1 枚硬币的结果就等价于先翻转任意 1 枚硬币,然后再翻转所有硬币。现在要把 N 枚正面朝上的硬币翻转成反面朝上,那么可以进行 N 次这样的操作,第 i 次操作等价于先翻转第 i 枚硬币,然后再翻转所有硬币(第 i 次操作具体实现则是翻转除了第 i 枚硬币以外的所有硬币)。因为 N 是一个偶数,将所有硬币全部翻转 N 次不会产生任何效果,所以经过 N 次这样的操作后,N 枚正面朝上的硬币就均被翻转成反面朝上了。

  • 那么为什么 N 是最小的操作次数呢

    假设存在只需要操作 M(0 < M < N) 次的方案,那么经过 M 次操作后,前 M 枚硬币翻转了 M - 1 次,后 N - M 枚硬币则翻转了 M 次,很显然 M - 1 和 M 分别是一奇一偶或者是一偶一奇,而翻转偶数次的硬币必然不会被翻转成反面朝上,所以不存在只需要操作 M 次的方案,即 N 就是最小的操作次数。

二、P2708 硬币翻转

题目描述:有很多个硬币摆在一行,有正面朝上的,也有背面朝上的。正面朝上的用 1 表示,背面朝上的用 0 表示。现在要求从这行的第一个硬币开始,将从第一个硬币开始的前若干个硬币同时翻面,求如果要将所有硬币翻到正面朝上,最少要进行这样的操作多少次?

题目链接:P2708 硬币翻转 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

代码实现一:

#include <stdio.h>
#include <string.h>

char str[1000000] = { 0 };

int main()
{	
	scanf("%s", str);
	int k = 0;  // k 表示要翻转的最少次数
	int i = 0;
	int len = strlen(str);
	for (i = 0; i < len - 1; i++)
	{
		if (str[i] != str[i + 1])  // 判断第 i 枚硬币的状态是否和第 i + 1 枚硬币相同
		{
			k++;  // 如果不相同,则把第 i 枚硬币以及它之前的若干枚硬币同时翻面
		}
	}
	if (str[len - 1] == '0')  // 此时所有硬币的状态都是 str[len - 1]
	{
		k++;  // 所以,如果最后一枚硬币的状态为 '0',则还要将所有的硬币翻转到正面
	}
	printf("%d\n", k);
	return 0;
}

思路一

现在要把所有硬币翻转到正面朝上,那么可以从第一枚硬币开始,判断这枚硬币的状态是否和它紧邻的右边的这枚硬币相同,如果不相同则把这枚硬币以及它之前的若干个枚硬币同时翻面,使这些硬币的状态和右边的这枚硬币相同。按此方式重复下去,直到找到最后一枚硬币,此时所有硬币的状态都相同,且都和最后一枚硬币的状态相同,那么如果最后一个硬币的状态为 '0',则还要将所有的硬币翻转到正面。

代码实现二:

#include <stdio.h>
#include <string.h>

char str[1000000] = { 0 };

int main()
{
	scanf("%s", str);
	int k = 0;  // k 表示要翻转的最少次数,即 f(c1, c2, ..., cn)
	int i = 0;
	int len = strlen(str);
	for (i = len - 1; i > 0; i--)
	{
		if (str[i] == '0' && str[i - 1] == '1')
		{
			k += 2;
		}
	}
	// 此时 f(c1, c2, ..., cn) = x + f(c1)
	if (str[0] == '0') 
	{
		k++;
	}
	printf("%d\n", k);
	return 0;
}

思路二

假设硬币序列为 (c1, c2, ..., cn),所需要翻转的最少次数为 f(c1, c2, ..., cn)。

c_n= '1' ,f(c_1, c_2, ..., c_n) = f(c_1, c_2, ..., c_{n-1})

   c_n = c_{n-1} = '0',f(c_1, c_2, ..., c_n) = f(c_1, c_2, ..., c_{n-1})

   c_n = '0' ,c_{n-1} = '1' ,f(c_1, c_2, ..., c_n) = f(c_1, c_2, ..., c_{n-1}) + 2

c_1 = 1,f(c1) = 0

    c_1 = 0,f(c1) = 1

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值