一个正八边形,每条边有0,1两种状态,每次改变一条边的状态会同时改变与它相邻的边的状态,输入八个数作为八边形的初始状态,问最少多少步可以把所有边都置为0?
测试用例:0 0 0 0 0 0 0 1 ;
输出 :5
这道题的关键在于,翻转的顺序是不重要的。给八条边编号0-7;1234的翻转结果和4321的结果是一样的。
故,如果一条边在一个翻转序列中出现两次,那么就可以消掉这两次。
如:12341234这个翻转序列相当于没有动!
理解了上面,我们就可以开始构建自己的最短翻转序列了。使用穷举法,从翻转一条边开始,直到八条边都被翻转过结束。
翻转序列的增加代码如下:
void Plus(vector<int>& NeedPlus)
{
//先判断要不要加一条翻转的边,当前数组全都是最大值时,说明已经穷举完NeedPlus.size()下的所有可能,增大size.
bool IsAllMax = true;
for (auto i : NeedPlus)
{
if (i != SIDE_NUM - 1)
IsAllMax = false;
}
if (IsAllMax)
{
NeedPlus.emplace_back(0);
for (int i = 0; i < NeedPlus.size(); ++i)
{
NeedPlus[i] = 0;
}
}
else
{
for (int i = NeedPlus.size() - 1; i > -1; --i)
{
if (NeedPlus[i] < SIDE_NUM - 1)
{
++NeedPlus[i];
//每次自增完毕要将后面的字符置0
for (int j = i + 1; j < NeedPlus.size(); ++j)
{
NeedPlus[j] = 0;
}
break;
}
}
}
return;
}
翻转某些边的代码如下,有两个重载,第二个参数输入是边,就翻转单个的边,输入的是数组,就翻转数组中的所有边。
void Reverse(vector<int>& sides, int i)
{
if (i > SIDE_NUM - 1)
{
return;
}
int pre = i - 1;
int back = (i + 1) % SIDE_NUM;
if (pre < 0)
{
pre = SIDE_NUM-1;
}
sides[pre] = 1 - sides[pre];
sides[i] = 1 - sides[i];
sides[back] = 1 - sides[back];
}
void Reverse(vector<int>& sides, vector<int>& WaitForChange)
{
for (auto i : WaitForChange)
{
Reverse(sides, i);
}
}
下面是判断是否全0的代码
bool IsAllZero(vector<int>& sides)
{
for (auto i : sides)
{
if (i != 0)
return false;
}
return true;
}
最后是主函数,acm模式,先接收一个数字代表有多少组输入数据,用一个数组存储八边形中每条边的状态。
int main()
{
int n;
vector<int> sides(SIDE_NUM,0);
cin >> n;
for (; n > 0; --n)
{
//获取每条边状态
for (int i = 0; i < sides.size(); ++i)
{
cin >> sides[i];
}
//tmp为了方便每次计算反转序列后恢复初始状态
vector<int> tmp = sides;
//NeedToChange里面存储翻转序列
vector<int> NeedToChange;
while (!IsAllZero(tmp))
{
tmp = sides;
Plus(NeedToChange);
Reverse(tmp, NeedToChange);
}
//最后翻转序列的长度就是最少的步数
cout << NeedToChange.size() << endl;
}
}
完整代码放到下面了,改SIDE_NUM可以适用到任意边型。
#include <iostream>
#include <vector>
using namespace std;
#define SIDE_NUM 8
void Reverse(vector<int>& sides, int i)
{
if (i > SIDE_NUM - 1)
{
return;
}
int pre = i - 1;
int back = (i + 1) % SIDE_NUM;
if (pre < 0)
{
pre = SIDE_NUM-1;
}
sides[pre] = 1 - sides[pre];
sides[i] = 1 - sides[i];
sides[back] = 1 - sides[back];
}
void Reverse(vector<int>& sides, vector<int>& WaitForChange)
{
for (auto i : WaitForChange)
{
Reverse(sides, i);
}
}
bool IsAllZero(vector<int>& sides)
{
for (auto i : sides)
{
if (i != 0)
return false;
}
return true;
}
void Plus(vector<int>& NeedPlus)
{
bool IsAllMax = true;
for (auto i : NeedPlus)
{
if (i != SIDE_NUM - 1)
IsAllMax = false;
}
if (IsAllMax)
{
NeedPlus.emplace_back(0);
for (int i = 0; i < NeedPlus.size(); ++i)
{
NeedPlus[i] = 0;
}
}
else
{
for (int i = NeedPlus.size() - 1; i > -1; --i)
{
if (NeedPlus[i] < SIDE_NUM - 1)
{
++NeedPlus[i];
for (int j = i + 1; j < NeedPlus.size(); ++j)
{
NeedPlus[j] = 0;
}
break;
}
}
}
return;
}
int main()
{
int n;
vector<int> sides(SIDE_NUM,0);
cin >> n;
for (; n > 0; --n)
{
for (int i = 0; i < sides.size(); ++i)
{
cin >> sides[i];
}
vector<int> tmp = sides;
vector<int> NeedToChange;
while (!IsAllZero(tmp))
{
tmp = sides;
Plus(NeedToChange);
Reverse(tmp, NeedToChange);
}
cout << NeedToChange.size() << endl;
}
}