题目背景
小明正在玩一个“翻硬币”的游戏。
题目描述
桌上放着排成一排的若干硬币。我们用 *
表示正面,用 o
表示反面(是小写字母,不是零),比如可能情形是 **oo***oooo
,如果同时翻转左边的两个硬币,则变为 oooo***oooo
。现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?
输入格式
两行等长字符串,分别表示初始状态和要达到的目标状态,每行长度小于 10001000。
数据保证一定存在至少一种方案可以从初始状态和要达到的目标状态。
输出格式
一个整数,表示最小操作步数。
输入输出样例
输入 #1复制
********** o****o****
输出 #1复制
5
输入 #2复制
*o**o***o*** *o***o**o***
输出 #2复制
1
需要明确的一个事实就是只用按顺序遍历,并且左右都一样(只和最远的两个不同点所在的区间有关)。至于具体原因则在最后的解法的解读里面。
import java.util.Scanner;
public class Main {
public static void main(String args[]) {
String x,y;
int num = 0;
char a[] = new char[1000];
char b[] = new char[1000];
try (Scanner sc = new Scanner(System.in)) {
x = sc.nextLine();
y = sc.nextLine();
}
for(int i =0; i<x.length();i++) {
a[i] = x.charAt(i);
b[i] = y.charAt(i);
}
for(int i =0; i<x.length();i++) {
if(a[i] == b[i]) {
continue;
}else {
a[i+1] =(a[i+1]=='*' ?'o':'*') ;
num++;
}
}
System.out.println(num);
}
}
在这个代码里面
a[i+1] =(a[i+1]=='*' ?'o':'*') ;
整一个被依据条件赋予了'o'或者'?'。并且要比下面这个情况(我写的)好多了:
package 练习; import java.util.; public class 第一次
{ public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
StringBuilder str1 = new StringBuilder();
StringBuilder str2 = new StringBuilder();
int m; for(int i = 1 ; i <= 1000; i++) {
if(str1.charAt(i) != str2.charAt(i)) {
if(str1.charAt(i) = '') str1.insert(i,'o');
else
str1.insert(i,'*');
if(i < 1000 && str1.charAt(i + 1) == '*')
str1.insert(i,'o');
else
str1.insert(i,'*');
m++;
i++;
}
}
}
}
一句顶我好几句。
由此可见这种根据条件选择的非此即彼的判断选择要用a > b ? x:y;这样的三元语句。
看了别人的解法:
- 由于不会有无解的情况,我们可以从左至右依次循环遍历 字符串的每一个字符,判断是不是与 串相应的字符相等。
- 若不相等,翻转这枚和下一枚(他右边的)硬币。
- 这里可以偷个懒,由于之后不会再用到这枚硬币,可以不翻,只翻下一枚。
#include<bits/stdc++.h> using namespace std; char a[1005]; char b[1005]; int main(){ cin>>a>>b; int len=strlen(a); int i=0,sum=0; while(i<len){ if(a[i]!=b[i]){ a[i+1]=(a[i+1]=='*'?'o':'*'); sum++; } i++; } cout<<sum; return 0; }
这样计算的量级减少了几个N(不看大的O(N),看具体的细节)
还有一种方法,虽然没有优化但是可以看看不同的角度下的人是如何理解这一个题目的:
#include<bits/stdc++.h>
using namespace std;
string a, b;
int ans, bg = -1, n;
int main(){
cin >> a >> b;
n = a.length();
for(int i = 0; i < n; i++){
if(a[i] != b[i]){
if(bg == -1){//找第一个不同的硬币
bg = i;
}else{
ans += (i - bg);//找到第二个不同的硬币
bg = -1;//接着找下一个不同段
}
}
}
cout << ans;
return 0;
}
这里其实就是运用了基本结构构建思想(就是欧拉解决戈尼斯堡七座桥难题的那个思想)**对oo、
*******对o*******o 、*****对o***o 、n 个*对o(这里n个'*')o结构其实都一样,而所有的硬币排序都可以被拆成这这样的结构,而这样的结构翻成对应的状态所需要的次数有规律(整个小结构字符数量减去1就是翻转次数),所以可以。
下面只是个人胡思乱想的爱好
另外,应当明确的一个事实就是我们写算法就是为了让计算机的for循环结构和while结构帮我们自动处理同一的大量的执行。还有一点就是它们必须都是满足某种规律的,然而有的题目就比如翻硬币是没有规律的(我指的是明显的使得程序直接按照最朴素的想法直接进行的),这时for和while(虽然不一定但依然是在for和while的基础上进行转化的)肯定还是要用到的。所以我们只能按部就班的直接翻硬币。这时往往是要从另外一个角度来看待这一个问题的。我们可以毫不怀疑的说就是这个直接从开头开始翻转了。因为像数学一样转化这个问题就说明它是有规律的,但是请注意到那些看可以被重写(重述)的问题是在基本的问题上进行改进的(因为这就和数学里面的“范畴论”一样,不同的问题之间之所以可以一步一步的转化就是因为它转化后的,如代数转几何是因为几何就是代数建立的基础)而算法的简化问题就是在之前的问题的认识之下(不是穷举)一一对应的结果(即使人们并未意识到),所以我们可以丝毫不怀疑的说就是要这样解决的。
但是用已有的去重构那些复杂的是很累的,所以数学中才会用那些高级的理论来解决成了那些基础数学很难解决的问题(高级理论放大了基础的一些特性,更高又在更高级的上面构造,(高级又有“自己的”特点,又可以深挖))。对应到同样是逻辑为基础的计算机里就是——高级算法和数据结构了。这样子我们就看清楚了哪些问题是值得我们去用算法和数据结构的。