题目
题目概述
在桌面上有一排硬币,共N枚,每一枚硬币均为正面朝上。现在要把所有的硬币翻转成反面朝上,规则是每次可翻转任意N−1枚硬币(正面向上的被翻转为反面向上,反之亦然)。求一个最短的操作序列(将每次翻转N-1枚硬币成为一次操作)。
输入格式
一个自然数N(N为不大于100的偶数)。
输出格式
第一行包含一个整数S,表示最少需要的操作次数。接下来的S行每行分别表示每次操作后桌上硬币的状态(一行包含N个整数(0或1),表示每个硬币的状态:0――正面向上,和1――反面向上,不允许出现多余空格)。
对于有多种操作方案的情况,则只需操作的字典序最小输出一种。
注:操作的字典序:对于一次操作,1表示翻转,0表示不翻转。
但是需要你输出的是每一次操作完的状态,0表示正面朝上,1表示反面朝上。
输入输出样例
输入
4
输出
4
0111
1100
0001
1111
思路讲解
自认为计算机网络_同学的讲解令人不甚满意,于是打算开始写一写自己的思路。
这题我认为首先应该注意的就是字典序。
对于数字1、2、3…n的排列,不同排列的先后关系是从左到右逐个比较对应的数字的先后来决定的。例如对于5个数字的排列 12354和12345,排列12345在前,排列12354在后。按照这样的规定,5个数字的所有的排列中最前面的是12345,最后面的是 54321。
本题中所涉及的字典序仅需要了解以上便足够。
题目中说到 “对于一次操作,1表示翻转,0表示不翻转。”
而每一次操作完成后的状态:“0表示正面朝上,1表示反面朝上。”
因此我们可以列出题目样例中的完成状态及每一状态所对应的操作。
状态 操作
0000 【0111】
0111 【1011】
1100 【1101】
0001 【1110】
1111
操作需要遵循字典序。
由于硬币状态仅有0和1两种选择,而初始状态为全0,所以第一次操作时,我们保持一个0不动,其余全部进行翻转。硬币排列的0,1顺序是无关紧要的,第一次选择一个0不翻转后,第二次若是还选择一个0不翻转,则会直接回到上一次同样的状态造成不必要的步骤浪费,因此下一次须选择一个1保持不翻转,而再下一次须保持一个0不翻转。
以上,我们再开始考虑字典序的问题。由于不翻转为0,翻转为1,每次操作选择一个不翻转的位置。初始状态全为0,因此第一次操作选择一个0不翻转,又根据字典序,选择(状态)从左到右的第一个0(位置为a[0]),所以操作为【0111】。
同理,第二次操作选择(状态)从左到右第一个1不翻转(位置为a[1]),即操作为【1011】。
至于最少次数的问题:共N枚硬币,每次翻转N-1枚,等价于每次翻转一枚。而题目规定N为偶数,所以当每枚硬币都翻转N-1次后,所有硬币就都翻转了。因此最少的次数为N。
代码
#include <bits/stdc++.h>
using namespace std;
int main(){
int n,j,flag=0;
//用flag来记录当前循环需要一个0还是1不翻转,flag初始化为0
int a[100]={};
scanf("%d",&n);
printf("%d\n",n);
for(int m=0;m<n;m++){//m次输出
for(j=0;j<n;j++){//扫描第一个flag的位置
if(a[j]==flag){
flag=!flag;
//转化一次flag,并用j记录flag的位置
break;
}
}
for(int i=0;i<n;i++){
if(i==j){//flag处不翻转
printf("%d",a[i]);
continue;
}
a[i]=!a[i];//其余进行翻转
printf("%d",a[i]);
}
printf("\n");
}
return 0;
}