目录
一、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)。
①
②