贪心(2)


  又是一个“充实”而又“美好”的一周,在如此愉悦的一周,我们贪心部分题目得以延期,我也非常“高兴”地还是没能完成~~
  反思一下,总是在代码实现中出现这样那样的问题,在脑海中对一个题形成一种思路后并没有再思考一下该怎样合理布局以实现自己的算法,而是匆匆忙忙下开始写代码,导致写着写着就会发现其它部分的漏洞需要去填补,有点顾此失彼,捉襟见肘。多花时间是一会事,而造成的有些错误是可以察觉的,而还有些潜在的错误寻找起来极其困难,通常弥补起来也并非易事,所以日后在代码的书写前,最好可以先考虑下具体布局,类似一种细节上的“伪代码”,或许可以减少下此类错误吧。
  言归正传,回到我们的贪心。
  在做过这么多(这里的多不是指数量上的多,至于到底什么个多,这你得品,细细的品…)的练习题目后,我却会重新问一下什么是贪心。我想问的当然不是指的它的定义,或者任何对于这一算法的描述,我在想的是为什么会有贪心这种算法,以及为什么在这类问题中产生贪心这种算法,这听起来有点滑稽,也有点莫名其妙。但委实说,这就是我在做题或者说处理问题时确实的困扰。每当我想出某一个问题的解题思路时,我都会在内心低产生一种疑问,这到底是不是贪心,我总感觉我在为得出某一个问题的答案时“无所不用其极”,而到最后不知道自己走的到底是条什么路。从对贪心的定义和各种描述中感觉,层层分解和每一步选取最优解,就好似你的代码中就应该是循环套if,else的形式,但难道贪心就只是这样一种机制吗?或者遇到诸如此类问题我们只能去依靠这种机制吗?毕竟,将来走入赛场,面对全新的题目,不可能会标注这个是贪心,那个是递归,哪个是递推,哪个是分治,这应该就是老师所说的希望我们不忘初心吧。
  谈谈做题,有个题让我印象深刻,像是自己被自己给耍了。
  在这里插入图片描述
  当我一看到这个题目时,就产生了一种想法:既然每次按按钮相当于同时按下三个。那么我想改变当前按钮的状态,完全可以靠按下某三个连续下标所对应的按钮,其中包含当前需要进行改变的按钮。再想,既然如此,何不依靠这种按法,尽量不去“打搅”某些按钮,而这些就是本就不需要按的按钮,最好的方法,就是当前需要改变的按钮即为这三个按钮的一边某个。这样,就自然而然的想到,通过这一方法,逐一判断数组元素,当遇到与目标数组不同的元素时即按下包含此下标在内的接下来三个连续的按钮,以维持前面的按钮状态不会发生变化。鉴于考虑到碰到倒数第二个元素时,后面只能有连续两个,所以只能依靠按下两个按钮的方式,看看能否实现转变,当然,既然最后面两个可以,前两个也可能,可以以前两个不同分情况,最后进行比较。于是,对数组元素的枚举就变成了下标从2到n-2。欣然提交,wrong answer!
  回头来看,幡然醒悟!对呀,如果只有一个怎么搞,这两种情况不能得出结果。加上特例的处理,欣然提交,wrong answer!
  纳闷了,错哪了!仔细思考,想到如果前两个相同便跳过,不同就转换前两个,怎样保证就是最优的呢,或许通过转换前三个能够得到不同的解或者有和无的区别。因此,这两种情况虽不同,但都应从第一个下表开始枚举。一拍大腿,欣然提交,wrong answer!
  本不想发代码的,因为一次一次修改,使得得出的程序冗杂,本可以更简洁的,但考虑到日后回来看到这个题总不能去翻陈年的提交记录吧,再者,日后看到当时如此青涩的代码也会微微一笑吧。

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;

void change(char &,char &,char &);
void way1();
void way2();
void out(int,int);
char a[35]={},b[35]={},c[35]={};
int times1=0,times2=0,n;
int main()
{
	cin>>a>>b;
	n=strlen(a);
	if(n==1)
	{
	    if(a[0]==b[0]) cout<<"0";
	    else cout<<"1";
	    return 0;
	}
	strcpy(c,b);
	way1();
	way2();
    if((a[n-2]==b[n-2]&&a[n-1]!=b[n-1])||(a[n-2]!=b[n-2]&&a[n-1]==b[n-1]))
    times1=-1;
    if((a[n-2]==c[n-2]&&a[n-1]!=c[n-1])||(a[n-2]!=c[n-2]&&a[n-1]==c[n-1]))
    times2=-1;
    out(times1,times2);
}
void change(char &x,char & y,char & z)
{
    if(x=='1') x='0';
    else x='1';
    if(y=='1') y='0';
    else y='1';
    if(z=='1') z='0';
    else z='1';
}
void way1()
{
    for(int i=0;i<n-2;i++)
        if(a[i]!=b[i])
        { change(b[i],b[i+1],b[i+2]); times1++; }
        if(a[n-2]!=b[n-2]&&a[n-1]!=b[n-1])
        ++times1;
}
void way2()
{
    times2++;
    if(c[0]=='1') c[0]='0';
    else c[0]='1';
    if(c[1]=='1') c[1]='0';
    else c[1]='1';
    for(int i=0;i<n-2;i++)
        if(a[i]!=c[i])
        { change(c[i],c[i+1],c[i+2]); times2++; }
        if(a[n-2]!=c[n-2]&&a[n-1]!=c[n-1])
        ++times2;
}
void out(int x,int y)
{
    if(x<0&&y<0) cout<<"impossible";
    if(x<0&&y>=0) cout<<y;
    if(x>=0&&y<0) cout<<x;
    if(x>=0&&y>=0)
    {
        if(x>=y) cout<<y;
        else cout<<x;
    }
}

再回来皱眉细看,归为一处后,根本没分开转换前两个的情况,这真是拆了东墙补西墙。长舒一口气,提交,ac,又长舒一口气!
  总共提交七次。回头看来,做题太急于求成了,看到一个契机,得到一个小的突破便急于看到结果,使得原本大好的趋势中途骤停,解题过程一波三折,费时费力不讨好。恰如学习,急于求成,有点小小的进步就急于看到成果,只会在这条道路上走的起起伏伏,蜿蜒曲折。推广及它,万事都是如此。啧啧,搞起哲学了。。。
  再看另一个题目。
  在这里插入图片描述
  首先看到这个题目,我先想到了一个想法。(以希望赢得尽可能多为例)先排序,再以“我”的牌为基准,逐一比较对方的牌,能赢就赢,同时记录下此时对方牌的下标+1,在比较我下一张牌,从对方上栗比较最终开始。因为我的较大的牌都只能赢过上次的下标,这张牌最多只能与之后的进行比较。这样循环,直到对方的牌到了最后一个。这期间,记录下能够赢的,和只能做到平局的。其中会架空许多张对方的牌,而这些只能和“我”那些小的牌比,所以只能是必输的。用总牌数减去能赢的和平局的就是输的。这时,便有这样的情况,一组平局的和必输的完全可以转化为一赢一输,在因得到奖励数不同,所以有必要进行这种转化。只需比较平的和输的数量,就可确定进行多少组转化(输和平必有一种完全转化)。这样,就可以得出最终答案。不过我干了一件蠢事,就是接着去搜了题解,想看看别人是不是也这么想的,才知道原来大佬们都是利用的田忌赛马的思考方法。最后因为我的思路写出的代码总是有错,便越来越觉得田忌赛马的思路好,最终放弃了。至于田忌赛马思路的解法,还是参见大佬们的代码吧。
  但不意味着这一方法就此烟消云散了,再有时间还要重来试试,验证它能否正确。毕竟嘛,老师说过什么不忘初心,我也还记得嘛,只不过现在水平低,而新的专题又来了,得另寻时间了。鉴于本人比较笨,即使贪心这一专题就要过去了,对贪心的练习和进一步体会远远还没结束,甚至现在顶多是开了个头。如此,再回到最初的那个问题,到底什么是贪心?我的结果是,没什么可说的,也没什么可问的,因为一个“贪”字便足以解释它到底怎么来的,到底怎么做的。如果还在犹豫你的方法到底是不是贪心,那就不用管了,你正在做的,就是贪心。贪心的题目可以不用贪心做,不是贪心的题目未必就不能用这一思想去考虑。学习这么多的算法,而又不被这些算法所禁锢,学习算法更是学习一种思想,学习一种能够通过代码实现对数据的控制,最终再以万变的控制去控制万变的数据,怀着曾经的热忱,通过钻研各种已有的算法,不变的永不放弃的恒心和毅力。
  啧啧,好像我已经是大佬了一样。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值