001:特殊密码锁

001:特殊密码锁

总时间限制:
1000ms
内存限制:
1024kB

描述

有一种特殊的二进制密码锁,由n个相连的按钮组成(n<30),按钮有凹/凸两种状态,用手按按钮会改变其状态。

然而让人头疼的是,当你按一个按钮时,跟它相邻的两个按钮状态也会反转。当然,如果你按的是最左或者最右边的按钮,该按钮只会影响到跟它相邻的一个按钮。

当前密码锁状态已知,需要解决的问题是,你至少需要按多少次按钮,才能将密码锁转变为所期望的目标状态。

输入
两行,给出两个由0、1组成的等长字符串,表示当前/目标密码锁状态,其中0代表凹,1代表凸。

输出
至少需要进行的按按钮操作次数,如果无法实现转变,则输出impossible。

分析:
枚举法,且第一位应分按下和不按下两种情况,因为这将影响后面的布局,比如数据1110,1111,若是单单判断到末尾才考虑将按下和不按下将为时已晚。

在网上看了两篇文章
第一篇:第1篇
他的代码可以通过测试。
这篇相当详尽,但有一些不足之处,对超过边界的未给予一个很好的约束,比如sw里面就有一些漏洞,仅仅用size来弄,在我看来是不能满足题意的。
第二篇:第2篇
他的代码也可以通过测试。
但有一些漏洞,就如评论所说1111,0000跑出1??,明显这是不对的,只能说openjudge的测试数据不完善,出现了漏网之鱼,但他的大意还是值得借鉴的,但相较于第一篇来说,他的漏洞实在太大。

我将第一篇的代码优化和简化了一下,他的代码提供了记录按下按钮的位置,我们可以将之去掉达到简化目的,除此之外,添加了很多注释供理解。

#include <iostream>
#include <cstring>
using namespace std;
const int MAX = 100;

void sw(char a[], int i, int s){
	if(i==0&&s>1)        //i为首位,i+1位取反
		a[i+1] ^= 1;
	else if(i==s-1)       //i为末位,i-1位取反
		a[i-1] ^= 1;
	else{               //i为中间位, i-1,i+1取反
		a[i+1] ^= 1;
		a[i-1] ^= 1;
	}
	a[i] ^= 1;
}

int main(){
	char ori[MAX];//原状态
	char v[MAX];//变化中的状态
	char result[MAX];//目标状态
	int num=0;//按下数
	cin >> ori >> result;
	int n = strlen(ori);
	for(int k=0; k<2; ++k){     //k有两个值,代表首位按不按下两种情况
        num=0;
		strcpy(v, ori);     //将原复制到变化中

		if(k==0){//当第一个开关为关闭状态时,也即不按下首位
			for(int i=0; i<n-1; ++i){
				if(v[i]!=result[i]){        //变化与目标不符
					sw(v, i+1, n);             //改变了变化状态
					num++;
				}
			}
			if(result[n-1]==v[n-1]) break;      //如果目标的最后一位和变化的最后一位相等,则退出for循环
		}
		else{//第一个开关为打开时, 也即按下首位
			sw(v, 0,n);
			num++;
			for(int i=0; i<n-1; ++i){
				if(v[i]!=result[i]){
					sw(v, i+1, n);
					num++;
				}
			}
		}
	}
	if(v[n-1]==result[n-1]){
		cout << num;
	}
	else
		cout << "impossible" << endl;
	return 0;
}

在这里,有一些有意思的点,比如:

  • 第2篇要去比较操作数的大小,这里明明没最小啊?
    实际上,k=0,不按首位肯定会比k=1按下首位少操作数,所以比较大小就没必要了,只要知道末尾相等不相等就行了,这里也正是第2篇错误的原因,原因分析: 1111,0000进入时,它首先按下2号位,当运行到4号位时,虽然不相等,但前一位相等,所以Change不了,这时num1=1;
    到了按下首位,得到num2=2,虽然是正确的,但由于取最小,导致min_num=1,所以错误。

  • 还有一点就是为什么只需要判断最后一位?
    这是因为在前面的判断中就已经判断过了,不同我们才按下的,所以最后一位将是成不成不成功的标志。

  • 枚举的精髓在于:找到局部,缩小枚举范围,省脑袋。

参考大佬文章的链接:
第1篇
第2篇

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值