一、项目背景
近日在观看某短视频软件时,发现某些用户直播移动火柴游戏,笔者有时会很快找出方法,有时却毫无头绪,如5+7=9,怀疑无解,故以此编程。
二、编程环境
笔者用的是 Visual Studio 2019,基于标准库编程。
三、规则介绍
题目给出一个十位数以内的自然数加减算式,三个数都为0-9可用火柴表示的单个数字,如:
其中+-为:
题目要求为移动一根火柴使原错误等式成立,如6-6=3,即可移动6上一根火柴,使6变为9,满足题意。
要求输入一个等式,输出满足题意的所有等式,若无,则输出无解。
四、思路解析
1、缺余量和移动数:
(1)定义;
由于游戏中,三个数字和运算符号也可以移动,所以考察当将数字i变为j时,针对这个数字,火柴变动的最少个数即为移动数,而变动后所多余或所缺失的火柴数目为缺余量,定义正数为多余量,负数为缺失量。
如:数字3变为数字9,至少需要移动1根火柴,而这一根火柴数字3没有,缺一根,故由3到9的变化中,移动数为1,缺余量为-1。
(2)数字火柴编码;
为了计算缺余量和移动数,笔者将火柴数字图中七个火柴位置编码,由低位到高位如图,得到了从数字0到数字9的二进制数组:
int Num[10] = { 126,48,109,121,51,91,95,112,127,123 };
例如0,即为0b1111110,十进制为126。所以Num[i]就是数字i的火柴编码数。
(3)计算;
i变为j:
缺失量=组成数字i的火柴数 - 组成数字j的火柴数。
data[i][j].suls = NumOfOne(Num[i]) - NumOfOne(Num[j]);
移动数=组成i,j火柴数较大的那个 - i,j公共火柴部分。
data[i][j].move = data[i][j].suls >= 0 ? (NumOfOne(Num[i]) - NumOfOne(Num[i] & Num[j])) : (NumOfOne(Num[j]) - NumOfOne(Num[i] & Num[j]));
其中NumOfOne()函数定义如下:
int NumOfOne(int n)
{
int cnt;
for (cnt = 0; n; cnt++)n = n & (n - 1);
return cnt;
}
2、计算改变自身的移动数和多余量的和即为移动木柴数
此处做了算法的延伸,可解决移动多根火柴的问题。
bool Flag(num num1, num num2, num num3, num sym, int n) {
//计算改变自身的移动数和多余量的和即为移动木柴数
bool a = num1.suls + num2.suls + num3.suls + sym.suls == 0;
num s[4] = { num1,num2,num3,sym };
int sum = 0;
for (int i = 0; i < 4; i++)
if (s[i].suls >= 0)
sum += s[i].move;
else
sum += s[i].move+s[i].suls;
return a && (sum == n);
}
3、遍历计算
void cul(char* ch) {
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
for (int k = 0; k < 10; k++)
for (int l = 0; l < 2; l++)
if (Flag(data[ch[0] - '0'][i], data[ch[2] - '0'][j], data[ch[4] - '0'][k], sym[(ch[1] / 2 - 21) * 2 + l], 1))
if ((l && (i - j == k)) || ((!l) && (i + j == k)))
printf("%d %c %d = %d\n", i, 43 + 2 * l, j, k);
}
五、代码
#include <iostream>
int Num[10] = { 126,48,109,121,51,91,95,112,127,123 };
struct num {
int suls;
int move;
}data[10][10], sym[4] = { {0,0},{1,1},{-1,1},{0,0} };
int NumOfOne(int n)
{
int cnt;
for (cnt = 0; n; cnt++)n = n & (n - 1);
return cnt;
}
bool IfNumber(char ch) {
return ch >= '0' && ch <= '9';
}
bool Flag(num num1, num num2, num num3, num sym, int n) {
//计算改变自身的移动数和多余量的和即为移动木柴数
bool a = num1.suls + num2.suls + num3.suls + sym.suls == 0;
num s[4] = { num1,num2,num3,sym };
int sum = 0;
for (int i = 0; i < 4; i++)
if (s[i].suls >= 0)
sum += s[i].move;
else
sum += s[i].move+s[i].suls;
return a && (sum == n);
}
void cul(char* ch) {
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
for (int k = 0; k < 10; k++)
for (int l = 0; l < 2; l++)
if (Flag(data[ch[0] - '0'][i], data[ch[2] - '0'][j], data[ch[4] - '0'][k], sym[(ch[1] / 2 - 21) * 2 + l], 1))
if ((l && (i - j == k)) || ((!l) && (i + j == k)))
printf("%d %c %d = %d\n", i, 43 + 2 * l, j, k);
}
int main()
{
char ch[10] = { 0 };
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
{
data[i][j].suls = NumOfOne(Num[i]) - NumOfOne(Num[j]);
data[i][j].move = data[i][j].suls >= 0 ? (NumOfOne(Num[i]) - NumOfOne(Num[i] & Num[j])) : (NumOfOne(Num[j]) - NumOfOne(Num[i] & Num[j]));
}
while (1) {
gets_s(ch);
if (IfNumber(ch[0]) && (ch[1] == '+' || ch[1] == '-') && IfNumber(ch[2]) && ch[3] == '=' && IfNumber(ch[4]))
cul(ch);
}
}
六、总结:
最后测试5+7=9确实无解,短视频误人。