七彩鹦鹉螺魔方

目录

七彩鹦鹉螺魔方

魔方三要素

根据对半翻转做角度推理

根据组合做角度推理

有趣的形态

所有形态枚举(V1)

最速复原跳转表(V1)

启发式策略(单节点)(V1)

启发式策略(双节点)(V1)

[刷新方案(V1 -> V2)](#刷新方案(V1 -> V2))

所有形态枚举(V2)

最速复原跳转表(V2)

启发式策略(单节点)(V2)

启发式策略(双节点)(V2)

启发式策略(三节点)(最终策略)


七彩鹦鹉螺魔方

魔方三要素

(1)组成部件

一共3层,每层7个不同的部件,按照从小到大分别是粉色,紫色,蓝色,绿色,黄色,橙色,红色。

(2)可执行操作

一共三种操作:旋转顶层、旋转底层、对半翻转。

为了方便,我们提出一个大操作的概念,即若干次旋转顶层+若干次旋转底层+1次对半翻转+若干次旋转顶层+若干次旋转底层,其中的若干可以为0。

显然,魔方的打乱和复原都是一系列的大操作。

(3)目标态

恢复鹦鹉螺的外形,即每个部件回到原本的位置。

根据对半翻转做角度推理

假设粉色,紫色,蓝色,绿色,黄色,橙色,红色分别是x1 x2 x3 x4 x5 x6 x7度,则x1+x2+x3+x4+x5+x6+x7=360

根据能完成对半翻转的场景,提出新的等式。

场景1:

则x1+x6+x7=180,x2+x3+x4+x5=180

场景2:

x1+x2+x3+x7=180,x4+x5+x6=180

场景3:

x7+x7=180

根据这3个场景,目前得到的方程组是:

x1=90-x6

x2+x3=x6

x4+x5=180-x6

x7=90

所以,还需要寻找3个条件。

场景4:

新增条件x3+x4=90

方程组变成:

x1=90-x6

x2=x6-x3

x4=90-x3

x5=90-x2

x7=90

所以,还需要寻找2个条件。

根据组合做角度推理

根据已有的5个方程:

x1=90-x6

x2=x6-x3

x4=90-x3

x5=90-x2

x7=90

我们先做出一个推测 x1到x7分别是20 30 40 50 60 70 90

那么,在其中选若干个数加起来能构成180的情况只有9种:

90+90=180

20+70+90=180

30+60+90=180

40+50+90=180

20+30+40+90=180

50+60+70=180

20+30+60+70=180

20+40+50+70=180

30+40+50+60=180

把这个转换成方程组就是:

x7+x7=180

x1+x6+x7=180

x2+x5+x7=180

x3+x4+x7=180

x1+x2+x3+x7=180

x4+x5+x6=180

x1+x2+x5+x6=180

x1+x3+x4+x6=180

x2+x3+x4+x5=180

不难发现,上述5个方程构成的不定方程组的任意解都是满足这9个方程的。

所以,无论具体角度是多少,只需要满足这5个方程,得到的七彩鹦鹉螺魔方一定是等价的。

既然如此,那么我们不妨设角度就是20 30 40 50 60 70 90

PS:生产厂家为了方便,大概率取的也就是这7个值

有趣的形态

所有形态枚举(V1)

中间层固定把红色(90度)放左上角,粉色紫色蓝色(20 30 40度)放左下角,那么竖直的线就是分割线。

按照时钟的顺序,顶层的初始状态是70 60 50 40 30 20 90,底层的初始状态是50 60 70 90 20 30 40

中间层的初始状态是0,唯一的非初始状态是1

cpp 复制代码
//通用的数据缓存去重编号方案
template<typename T>
class GetSingleId
{
public:
	int id(T x) //所有元素按照首次传入顺序编号0,1,2......
	{
		auto it = m.find(x);
		if (it != m.end())return it->second;
		mr[n] = x;
		return m[x] = n++;
	}
	int num() //id()==num()-1表示是新元素,否则是重复元素
	{
		return n;
	}
	T getData(int id) //根据id获取元素数据
	{
		return mr[id];
	}
private:
	map<T, int>m;
	map<int, T>mr;
	int n = 0;
};

struct Node {
	vector<int>up{ 70,60,50,40,30,20,90 };
	int mid = 0;
	vector<int>down{ 50,60,70,90,20,30,40 };
	int ans = 0;
};

int getId(vector<int> v) {
	static GetSingleId<vector<int>> opt;
	int ans = opt.id(v);
	int len = v.size();
	while (len--) {
		v.push_back(v[0]);
		v.erase(v.begin());
		opt.id(v);
	}
	return ans;
}
int getId(const Node &nod)
{
	static GetSingleId<vector<int>> opt;
	int id1 = getId(nod.up);
	int id2 = getId(nod.down);
	return opt.id(vector<int>{id1, nod.mid, id2});
}

vector<int> findId(const vector<int>&v)
{
	vector<int>ans;
	for (int i = 0; i < v.size(); i++) {
		int s = 0;
		for (int j = i; j < i + v.size(); j++) {
			s += v[j%v.size()];
			if (s >= 180)break;
		}
		if (s == 180)ans.push_back(i);
	}
	return ans;
}

vector<Node> getNext(const Node &nod) 
{
	vector<int> id1 = findId(nod.up);
	vector<int> id2 = findId(nod.down);
	vector<Node> ans;
	for (int i : id1) {
		for (int j : id2) {
			vector<int>v1, v2;
			int k1 = i, k2 = j, s1 = 0, s2 = 0;
			while (s1 < 180) {
				v1.push_back(nod.down[k2]);
				s1 += nod.down[k2];
				k2 = (k2 + 1) % nod.down.size();
			}
			while (s2 < 180) {
				v2.push_back(nod.up[k1]);
				s2 += nod.up[k1];
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k1 != i) {
				v1.push_back(nod.up[k1]);
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k2 != j) {
				v2.push_back(nod.down[k2]);
				k2 = (k2 + 1) % nod.down.size();
			}
			ans.push_back({ v1,1 - nod.mid,v2,nod.ans + 1 });
		}
	}
	return ans;
}


//假设对半翻转操作是长度为1,另外2个操作是长度为0,对整个图进行bfs搜出单源最短路径
void bfs()
{
	Node nod;
	set<int>s;
	s.insert(getId(nod));
	queue<Node>q;
	q.push(nod);
	int num = 0;
	while (!q.empty()) {
		auto nod = q.front();
		num++;
		for (int x : nod.up)cout << x << " ";
		cout << endl << nod.mid << endl;
		for (int x : nod.down)cout << x << " ";
		cout << endl << nod.ans<<endl << endl;
		q.pop();
		auto v = getNext(nod);
		for (auto nod : v) {
			int id = getId(nod);
			if (s.find(id) == s.end()) {
				s.insert(id);
				q.push(nod);
			}
		}
	}
	cout << num;
}

int main() {
	bfs();
	return 0;
}

输出:

......

30 60 30 60 40 90 50

0

90 70 20 20 50 40 70

14

20 50 40 70 40 90 50

0

90 70 20 30 60 30 60

14

4032

一共4032种不同的形态,其中任意2个形态之间的转换至少需要1次大操作。

也就是说,只旋转顶层、旋转底层得到的状态已经合并了,合并后的总数是4032=63*64

显然,这个数字通过组合的方法去推也是能推出来的,当然前提是对于可达状态集有了判定方法。

最速复原跳转表(V1)

前面的输出结果已经表明,最多需要14次大操作才能复原。

这里给出具体的操作路径:

cpp 复制代码
//通用的数据缓存去重编号方案
template<typename T>
class GetSingleId
{
public:
	int id(T x) //所有元素按照首次传入顺序编号0,1,2......
	{
		auto it = m.find(x);
		if (it != m.end())return it->second;
		mr[n] = x;
		return m[x] = n++;
	}
	int num() //id()==num()-1表示是新元素,否则是重复元素
	{
		return n;
	}
	T getData(int id) //根据id获取元素数据
	{
		return mr[id];
	}
private:
	map<T, int>m;
	map<int, T>mr;
	int n = 0;
};

struct Node {
	vector<int>up{ 70,60,50,40,30,20,90 };
	int mid = 0;
	vector<int>down{ 50,60,70,90,20,30,40 };
	int ans = 0;
	int id = 0;
	int fa = 0;
};

int getId(vector<int> v) {
	static GetSingleId<vector<int>> opt;
	int ans = opt.id(v);
	int len = v.size();
	while (len--) {
		v.push_back(v[0]);
		v.erase(v.begin());
		opt.id(v);
	}
	return ans;
}
int getId(const Node &nod)
{
	static GetSingleId<vector<int>> opt;
	int id1 = getId(nod.up);
	int id2 = getId(nod.down);
	return opt.id(vector<int>{id1, nod.mid, id2});
}

vector<int> findId(const vector<int>&v)
{
	vector<int>ans;
	for (int i = 0; i < v.size(); i++) {
		int s = 0;
		for (int j = i; j < i + v.size(); j++) {
			s += v[j%v.size()];
			if (s >= 180)break;
		}
		if (s == 180)ans.push_back(i);
	}
	return ans;
}

vector<Node> getNext(const Node &nod) 
{
	vector<int> id1 = findId(nod.up);
	vector<int> id2 = findId(nod.down);
	vector<Node> ans;
	for (int i : id1) {
		for (int j : id2) {
			vector<int>v1, v2;
			int k1 = i, k2 = j, s1 = 0, s2 = 0;
			while (s1 < 180) {
				v1.push_back(nod.down[k2]);
				s1 += nod.down[k2];
				k2 = (k2 + 1) % nod.down.size();
			}
			while (s2 < 180) {
				v2.push_back(nod.up[k1]);
				s2 += nod.up[k1];
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k1 != i) {
				v1.push_back(nod.up[k1]);
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k2 != j) {
				v2.push_back(nod.down[k2]);
				k2 = (k2 + 1) % nod.down.size();
			}
			ans.push_back({ v1,1 - nod.mid,v2,nod.ans + 1,0,0 });
		}
	}
	return ans;
}


//假设对半翻转操作是长度为1,另外2个操作是长度为0,对整个图进行bfs搜出单源最短路径
void bfs()
{
	Node nod;
	set<int>s;
	s.insert(getId(nod));
	queue<Node>q;
	q.push(nod);
	while (!q.empty()) {
		auto nod = q.front();
		int fa = nod.id;
		cout << "序号="<<nod.id << "     ";
		for (int x : nod.up)cout << x << " ";
		cout << "        " << nod.mid << "       ";
		for (int x : nod.down)cout << x << " ";
		cout << "   跳转到序号" << nod.fa << endl;
		q.pop();
		auto v = getNext(nod);
		for (auto nod : v) {
			int id = getId(nod);
			if (s.find(id) == s.end()) {
				s.insert(id);
				nod.id = id;
				nod.fa = fa;
				q.push(nod);
			}
		}
	}
}

int main() {
	bfs();
	return 0;
}

输出结果:

cpp 复制代码
序号=0     70 60 50 40 30 20 90         0       50 60 70 90 20 30 40    跳转到序号0
序号=1     50 60 70 40 30 20 90         1       70 60 50 90 20 30 40    跳转到序号0
序号=2     70 90 20 40 30 20 90         1       70 60 50 30 40 50 60    跳转到序号0
序号=3     90 20 30 40 40 30 20 90         1       70 60 50 50 60 70    跳转到序号0
序号=4     30 40 50 60 40 30 20 90         1       70 60 50 70 90 20    跳转到序号0
序号=5     50 60 70 20 90 70         1       60 50 40 30 90 20 30 40    跳转到序号0
序号=6     70 90 20 20 90 70         1       60 50 40 30 30 40 50 60    跳转到序号0
序号=7     90 20 30 40 20 90 70         1       60 50 40 30 50 60 70    跳转到序号0
。。。。。。
序号=4021     20 50 40 70 30 60 30 60         0       40 90 50 90 70 20    跳转到序号3887
序号=4022     90 70 20 40 90 50         0       30 60 30 60 20 50 40 70    跳转到序号3887
序号=4023     20 50 40 70 40 90 50         0       30 60 30 60 90 70 20    跳转到序号3887
序号=4024     30 60 30 60 90 70 20         0       20 50 40 70 40 90 50    跳转到序号3890
序号=4025     40 90 50 90 70 20         0       20 50 40 70 30 60 30 60    跳转到序号3890
序号=4026     30 60 30 60 20 50 40 70         0       90 70 20 40 90 50    跳转到序号3890
序号=4027     40 90 50 20 50 40 70         0       90 70 20 30 60 30 60    跳转到序号3890
序号=4028     30 60 30 60 90 70 20         0       40 90 50 20 50 40 70    跳转到序号3891
序号=4029     20 50 40 70 90 70 20         0       40 90 50 30 60 30 60    跳转到序号3891
序号=4030     30 60 30 60 40 90 50         0       90 70 20 20 50 40 70    跳转到序号3891
序号=4031     20 50 40 70 40 90 50         0       90 70 20 30 60 30 60    跳转到序号3891

篇幅太长无法发表,省略。

使用示例:

那么这就是 90 90 40 40 50 30 20 0 70 60 20 30 60 70 50 (需要适当的旋转才能在这4032个情况中找到唯一对应的那个)

对应的答案就是:

序号=2102 90 90 40 40 50 30 20 0 70 60 20 30 60 70 50 跳转到序号1495

序号=1495 20 30 40 40 50 30 20 70 60 1 90 90 60 70 50 跳转到序号753

序号=753 30 20 70 60 90 90 0 50 60 70 50 20 30 40 40 跳转到序号186

序号=186 50 60 70 90 90 1 20 30 40 40 30 20 70 60 50 跳转到序号17

序号=17 90 20 30 40 40 30 20 90 0 50 60 70 70 60 50 跳转到序号1

序号=1 50 60 70 40 30 20 90 1 70 60 50 90 20 30 40 跳转到序号0

启发式策略(单节点)(V1)

首先我们想到的策略自然是合并。

然后基于上面代码输出的4032个具体状态,我很快锁定了50+60这个组合。

(一开始我想的是类似于20+30+40或者40+50这种组合,但是并没有明确的发现)

用代码来验证一下:

cpp 复制代码
//通用的数据缓存去重编号方案
template<typename T>
class GetSingleId
{
public:
	int id(T x) //所有元素按照首次传入顺序编号0,1,2......
	{
		auto it = m.find(x);
		if (it != m.end())return it->second;
		mr[n] = x;
		return m[x] = n++;
	}
	int num() //id()==num()-1表示是新元素,否则是重复元素
	{
		return n;
	}
	T getData(int id) //根据id获取元素数据
	{
		return mr[id];
	}
private:
	map<T, int>m;
	map<int, T>mr;
	int n = 0;
};

struct Node {
	vector<int>up{ 70,60,50,40,30,20,90 };
	int mid = 0;
	vector<int>down{ 50,60,70,90,20,30,40 };
	int ans = 0;
	int id = 0;
	int fa = 0;
};

int getId(vector<int> v) {
	static GetSingleId<vector<int>> opt;
	int ans = opt.id(v);
	int len = v.size();
	while (len--) {
		v.push_back(v[0]);
		v.erase(v.begin());
		opt.id(v);
	}
	return ans;
}
int getId(const Node& nod)
{
	static GetSingleId<vector<int>> opt;
	int id1 = getId(nod.up);
	int id2 = getId(nod.down);
	return opt.id(vector<int>{id1, nod.mid, id2});
}

vector<int> findId(const vector<int>& v)
{
	vector<int>ans;
	for (int i = 0; i < v.size(); i++) {
		int s = 0;
		for (int j = i; j < i + v.size(); j++) {
			s += v[j % v.size()];
			if (s >= 180)break;
		}
		if (s == 180)ans.push_back(i);
	}
	return ans;
}

vector<Node> getNext(const Node& nod)
{
	vector<int> id1 = findId(nod.up);
	vector<int> id2 = findId(nod.down);
	vector<Node> ans;
	for (int i : id1) {
		for (int j : id2) {
			vector<int>v1, v2;
			int k1 = i, k2 = j, s1 = 0, s2 = 0;
			while (s1 < 180) {
				v1.push_back(nod.down[k2]);
				s1 += nod.down[k2];
				k2 = (k2 + 1) % nod.down.size();
			}
			while (s2 < 180) {
				v2.push_back(nod.up[k1]);
				s2 += nod.up[k1];
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k1 != i) {
				v1.push_back(nod.up[k1]);
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k2 != j) {
				v2.push_back(nod.down[k2]);
				k2 = (k2 + 1) % nod.down.size();
			}
			ans.push_back({ v1,1 - nod.mid,v2,nod.ans + 1,0,0 });
		}
	}
	return ans;
}

//假设对半翻转操作是长度为1,另外2个操作是长度为0,对整个图进行bfs搜出单源最短路径
vector<int> bfs(vector<Node>&vn)
{
	vector<int> ans(4032);
	Node nod;
	set<int>s;
	s.insert(getId(nod));
	queue<Node>q;
	q.push(nod);
	while (!q.empty()) {
		auto nod = q.front();
		int fa = nod.id;
		int newAns = nod.ans + 1;
		ans[fa] = nod.fa;
		vn.push_back(nod);
		q.pop();
		auto v = getNext(nod);
		for (auto nod : v) {
			int id = getId(nod);
			if (s.find(id) == s.end()) {
				s.insert(id);
				nod.id = id;
				nod.fa = fa;
				nod.ans = newAns;
				q.push(nod);
			}
		}
	}
	return ans;
}

bool check56(const vector<int>& v)
{
	int s50 = 0, s60 = 0;
	for (auto x : v) {
		if (x == 50)s50++;
		if (x == 60)s60++;
	}
	if (s50 != s60)return false;
	for (int i = 0; i < v.size(); i++) {
		if (v[i] != 50 && v[i] != 60)continue;
		if (v[(i + 1) % v.size()] != 110 - v[i] && v[(i + v.size() - 1) % v.size()]  != 110 - v[i])return false;
	}
	return true;
}
bool check56(const Node& nod)
{
	return check56(nod.up) && check56(nod.down);
}

int getLen(vector<Node>& vn, vector<int>& fa, int id)
{
	int s = 0;
	while (!check56(vn[id])) {
		s++;
		id = fa[id];
	}
	return s;
}

int main() {
	vector<Node> vn;
	vector<int> fa = bfs(vn);
	for (int i = 0; i < fa.size(); i++) {
		cout << i << " " << fa[i] << endl;
	}
	int max1 = 0, max2 = 0, maxs = 0;
	for (int i = 0; i < fa.size(); i++) {
		int x = getLen(vn, fa, i);
		max1 = max(max1, x);
		max2 = max(max2, vn[i].ans - x);
		maxs = max(maxs, vn[i].ans);
	}
	cout << endl << "max:" << max1 << " " << max2 << " " << maxs;
	return 0;
}

输出:

。。。。。。

4025 3890

4026 3890

4027 3890

4028 3891

4029 3891

4030 3891

4031 3891

max:11 7 14

所以我们得到结论:所有状态的最速复原都可以分为2步,第一步是完成2组50+60的合并,第二步是彻底复原。其中的第一步,最多需要11次大操作,其中的第二步,最多需要7次大操作,而总共最多需要14次大操作。

实际上,不管选什么状态作为跳转节点,只要终点是满足状态的,我们都可以说,最速复原都可以分为2步,第一步是***,第二步是***,第一步最多需要x次大操作,第二步最多需要y次大操作,而总共最多需要14次操作。

其中,x和y都是不超过14的。x和y越大,则这个启发式策略越不具备启发性,如果x或y等于14,则完全不具备启发性。

启发式策略(双节点)(V1)

我们对上面的策略做一个细化、强化

cpp 复制代码
//通用的数据缓存去重编号方案
template<typename T>
class GetSingleId
{
public:
	int id(T x) //所有元素按照首次传入顺序编号0,1,2......
	{
		auto it = m.find(x);
		if (it != m.end())return it->second;
		mr[n] = x;
		return m[x] = n++;
	}
	int num() //id()==num()-1表示是新元素,否则是重复元素
	{
		return n;
	}
	T getData(int id) //根据id获取元素数据
	{
		return mr[id];
	}
private:
	map<T, int>m;
	map<int, T>mr;
	int n = 0;
};

struct Node {
	vector<int>up{ 70,60,50,40,30,20,90 };
	int mid = 0;
	vector<int>down{ 50,60,70,90,20,30,40 };
	int ans = 0;
	int id = 0;
	int fa = 0;
};

int getId(vector<int> v) {
	static GetSingleId<vector<int>> opt;
	int ans = opt.id(v);
	int len = v.size();
	while (len--) {
		v.push_back(v[0]);
		v.erase(v.begin());
		opt.id(v);
	}
	return ans;
}
int getId(const Node& nod)
{
	static GetSingleId<vector<int>> opt;
	int id1 = getId(nod.up);
	int id2 = getId(nod.down);
	return opt.id(vector<int>{id1, nod.mid, id2});
}

vector<int> findId(const vector<int>& v)
{
	vector<int>ans;
	for (int i = 0; i < v.size(); i++) {
		int s = 0;
		for (int j = i; j < i + v.size(); j++) {
			s += v[j % v.size()];
			if (s >= 180)break;
		}
		if (s == 180)ans.push_back(i);
	}
	return ans;
}

vector<Node> getNext(const Node& nod)
{
	vector<int> id1 = findId(nod.up);
	vector<int> id2 = findId(nod.down);
	vector<Node> ans;
	for (int i : id1) {
		for (int j : id2) {
			vector<int>v1, v2;
			int k1 = i, k2 = j, s1 = 0, s2 = 0;
			while (s1 < 180) {
				v1.push_back(nod.down[k2]);
				s1 += nod.down[k2];
				k2 = (k2 + 1) % nod.down.size();
			}
			while (s2 < 180) {
				v2.push_back(nod.up[k1]);
				s2 += nod.up[k1];
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k1 != i) {
				v1.push_back(nod.up[k1]);
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k2 != j) {
				v2.push_back(nod.down[k2]);
				k2 = (k2 + 1) % nod.down.size();
			}
			ans.push_back({ v1,1 - nod.mid,v2,nod.ans + 1,0,0 });
		}
	}
	return ans;
}

//假设对半翻转操作是长度为1,另外2个操作是长度为0,对整个图进行bfs搜出单源最短路径
vector<int> bfs(vector<Node>&vn)
{
	vector<int> ans(4032);
	Node nod;
	set<int>s;
	s.insert(getId(nod));
	queue<Node>q;
	q.push(nod);
	while (!q.empty()) {
		auto nod = q.front();
		int fa = nod.id;
		int newAns = nod.ans + 1;
		ans[fa] = nod.fa;
		vn.push_back(nod);
		q.pop();
		auto v = getNext(nod);
		for (auto nod : v) {
			int id = getId(nod);
			if (s.find(id) == s.end()) {
				s.insert(id);
				nod.id = id;
				nod.fa = fa;
				nod.ans = newAns;
				q.push(nod);
			}
		}
	}
	return ans;
}

bool check5656(const vector<int>& v)
{
	int s50 = 0, s60 = 0;
	for (auto x : v) {
		if (x == 50)s50++;
		if (x == 60)s60++;
	}
	if (s50 != s60)return false;
	for (int i = 0; i < v.size(); i++) {
		if (v[i] != 50 && v[i] != 60)continue;
		if (v[(i + 1) % v.size()] != 110 - v[i] && v[(i + v.size() - 1) % v.size()] != 110 - v[i])return false;
	}
	return true;
}
bool check5656(const Node& nod)
{
	return check5656(nod.up) && check5656(nod.down);
}
bool check56(const vector<int>& v)
{
	for (int i = 0; i < v.size(); i++) {
		if (v[i] != 50 && v[i] != 60)continue;
		if (v[(i + 1) % v.size()] == 110 - v[i])return true;
	}
	return false;
}
bool check56(const Node& nod)
{
	return check56(nod.up) || check56(nod.down);
}

void getLen(vector<Node>& vn, vector<int>& fa, int id,int &len1,int &len2)
{
	int id0 = id;
	len1 = 0, len2 = 0;
	while (!check56(vn[id])) {
		len1++;
		id = fa[id];
	}
	while (!check5656(vn[id])) {
		len2++;
		id = fa[id];
	}
	cout << id0<<" "<< len1 << " " << len2 << " " << vn[id0].ans - len1 - len2 << endl;
}

int main() {
	vector<Node> vn;
	vector<int> fa = bfs(vn);
	for (int i = 0; i < fa.size(); i++) {
		cout << i << " " << fa[i] << endl;
	}
	int max1 = 0, max2 = 0, max3=0,maxs = 0;
	for (int i = 0; i < fa.size(); i++) {
		int len1, len2;
		getLen(vn, fa, i, len1, len2);
		max1 = max(max1, len1);
		max2 = max(max2, len2);
		max3 = max(max3, vn[i].ans - len1 - len2);
		maxs = max(maxs, vn[i].ans);
	}
	cout << endl << "max:" << max1 << " " << max2 << " "<< max3<<" " << maxs;
	return 0;
}

输出:

。。。。。。

4012 1 10 3

4013 1 10 3

4014 1 10 3

4015 1 10 3

4016 8 3 3

4017 8 3 3

4018 8 3 3

4019 8 3 3

4020 8 3 3

4021 8 3 3

4022 8 3 3

4023 8 3 3

4024 8 3 3

4025 8 3 3

4026 8 3 3

4027 8 3 3

4028 8 3 3

4029 8 3 3

4030 8 3 3

4031 8 3 3

max:8 11 7 14

结果并不理想,数字还是比较大。

但是不难发现,只有极少数是形如1 10 3这样的极端数据,大部分都是8 3 3 这样比较好的数据。

对这些少部分数据进行进一步分析,也没有明显的发现。

此时,我猜想这个双节点策略可能是有效的,问题可能出在由fa组成的树,这棵树蕴含的只是其中一种最优解,不包含所有最优解的信息。

所以,我们需要重新做BFS。

重新编码时,发现对状态进行去重编号的方案有问题,以下是刷新结果。

刷新方案(V1 -> V2)

只需要更新这一个函数(另外4032这个魔鬼数字也需要改掉)

cpp 复制代码
int getId(vector<int> v) {
	static map< vector<int>, int>m;
	if (m.find(v) != m.end())return m[v];
	int ans = m.size();
	int len = v.size();
	while (len--) {
		v.push_back(v[0]);
		v.erase(v.begin());
		m[v] = ans;
	}
	return ans;
}

所有形态枚举(V2)

cpp 复制代码
//通用的数据缓存去重编号方案
template<typename T>
class GetSingleId
{
public:
	int id(T x) //所有元素按照首次传入顺序编号0,1,2......
	{
		auto it = m.find(x);
		if (it != m.end())return it->second;
		mr[n] = x;
		return m[x] = n++;
	}
	int num() //id()==num()-1表示是新元素,否则是重复元素
	{
		return n;
	}
	T getData(int id) //根据id获取元素数据
	{
		return mr[id];
	}
private:
	map<T, int>m;
	map<int, T>mr;
	int n = 0;
};

struct Node {
	vector<int>up{ 70,60,50,40,30,20,90 };
	int mid = 0;
	vector<int>down{ 50,60,70,90,20,30,40 };
	int ans = 0;
};

int getId(vector<int> v) {
	static map< vector<int>, int>m;
	if (m.find(v) != m.end())return m[v];
	int ans = m.size();
	int len = v.size();
	while (len--) {
		v.push_back(v[0]);
		v.erase(v.begin());
		m[v] = ans;
	}
	return ans;
}
int getId(const Node &nod)
{
	static GetSingleId<vector<int>> opt;
	int id1 = getId(nod.up);
	int id2 = getId(nod.down);
	return opt.id(vector<int>{id1, nod.mid, id2});
}

vector<int> findId(const vector<int>&v)
{
	vector<int>ans;
	for (int i = 0; i < v.size(); i++) {
		int s = 0;
		for (int j = i; j < i + v.size(); j++) {
			s += v[j%v.size()];
			if (s >= 180)break;
		}
		if (s == 180)ans.push_back(i);
	}
	return ans;
}

vector<Node> getNext(const Node &nod)
{
	vector<int> id1 = findId(nod.up);
	vector<int> id2 = findId(nod.down);
	vector<Node> ans;
	for (int i : id1) {
		for (int j : id2) {
			vector<int>v1, v2;
			int k1 = i, k2 = j, s1 = 0, s2 = 0;
			while (s1 < 180) {
				v1.push_back(nod.down[k2]);
				s1 += nod.down[k2];
				k2 = (k2 + 1) % nod.down.size();
			}
			while (s2 < 180) {
				v2.push_back(nod.up[k1]);
				s2 += nod.up[k1];
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k1 != i) {
				v1.push_back(nod.up[k1]);
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k2 != j) {
				v2.push_back(nod.down[k2]);
				k2 = (k2 + 1) % nod.down.size();
			}
			ans.push_back({ v1,1 - nod.mid,v2,nod.ans + 1 });
		}
	}
	return ans;
}


//假设对半翻转操作是长度为1,另外2个操作是长度为0,对整个图进行bfs搜出单源最短路径
void bfs()
{
	Node nod;
	set<int>s;
	s.insert(getId(nod));
	queue<Node>q;
	q.push(nod);
	int num = 0;
	while (!q.empty()) {
		auto nod = q.front();
		num++;
		for (int x : nod.up)cout << x << " ";
		cout << endl << nod.mid << endl;
		for (int x : nod.down)cout << x << " ";
		cout << endl << nod.ans << endl << endl;
		q.pop();
		auto v = getNext(nod);
		for (auto nod : v) {
			int id = getId(nod);
			if (s.find(id) == s.end()) {
				s.insert(id);
				q.push(nod);
			}
		}
	}
	cout << num;
}

int main() {
	bfs();
	return 0;
}

输出:

。。。。。。

40 90 50 30 60 30 60

1

90 70 20 20 50 40 70

13

20 50 40 70 90 70 20

1

30 60 30 60 40 90 50

13

40 90 50 90 70 20

1

30 60 30 60 20 50 40 70

13

652

一共652种不同的形态,其中任意2个形态之间的转换至少需要1次大操作。

最速复原跳转表(V2)

前面的输出结果已经表明,最多需要13次大操作才能复原。

这里给出具体的操作路径:

cpp 复制代码
//通用的数据缓存去重编号方案
template<typename T>
class GetSingleId
{
public:
	int id(T x) //所有元素按照首次传入顺序编号0,1,2......
	{
		auto it = m.find(x);
		if (it != m.end())return it->second;
		mr[n] = x;
		return m[x] = n++;
	}
	int num() //id()==num()-1表示是新元素,否则是重复元素
	{
		return n;
	}
	T getData(int id) //根据id获取元素数据
	{
		return mr[id];
	}
private:
	map<T, int>m;
	map<int, T>mr;
	int n = 0;
};

struct Node {
	vector<int>up{ 70,60,50,40,30,20,90 };
	int mid = 0;
	vector<int>down{ 50,60,70,90,20,30,40 };
	int ans = 0;
	int id = 0;
	int fa = 0;
};

int getId(vector<int> v) {
	static map< vector<int>, int>m;
	if (m.find(v) != m.end())return m[v];
	int ans = m.size();
	int len = v.size();
	while (len--) {
		v.push_back(v[0]);
		v.erase(v.begin());
		m[v] = ans;
	}
	return ans;
}
int getId(const Node &nod)
{
	static GetSingleId<vector<int>> opt;
	int id1 = getId(nod.up);
	int id2 = getId(nod.down);
	return opt.id(vector<int>{id1, nod.mid, id2});
}

vector<int> findId(const vector<int>&v)
{
	vector<int>ans;
	for (int i = 0; i < v.size(); i++) {
		int s = 0;
		for (int j = i; j < i + v.size(); j++) {
			s += v[j%v.size()];
			if (s >= 180)break;
		}
		if (s == 180)ans.push_back(i);
	}
	return ans;
}

vector<Node> getNext(const Node &nod)
{
	vector<int> id1 = findId(nod.up);
	vector<int> id2 = findId(nod.down);
	vector<Node> ans;
	for (int i : id1) {
		for (int j : id2) {
			vector<int>v1, v2;
			int k1 = i, k2 = j, s1 = 0, s2 = 0;
			while (s1 < 180) {
				v1.push_back(nod.down[k2]);
				s1 += nod.down[k2];
				k2 = (k2 + 1) % nod.down.size();
			}
			while (s2 < 180) {
				v2.push_back(nod.up[k1]);
				s2 += nod.up[k1];
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k1 != i) {
				v1.push_back(nod.up[k1]);
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k2 != j) {
				v2.push_back(nod.down[k2]);
				k2 = (k2 + 1) % nod.down.size();
			}
			ans.push_back({ v1,1 - nod.mid,v2,nod.ans + 1,0,0 });
		}
	}
	return ans;
}


//假设对半翻转操作是长度为1,另外2个操作是长度为0,对整个图进行bfs搜出单源最短路径
void bfs()
{
	Node nod;
	set<int>s;
	s.insert(getId(nod));
	queue<Node>q;
	q.push(nod);
	while (!q.empty()) {
		auto nod = q.front();
		int fa = nod.id;
		cout << "序号=" << nod.id << "     ";
		for (int x : nod.up)cout << x << " ";
		cout << "        " << nod.mid << "       ";
		for (int x : nod.down)cout << x << " ";
		cout << "   跳转到序号" << nod.fa << endl;
		q.pop();
		auto v = getNext(nod);
		for (auto nod : v) {
			int id = getId(nod);
			if (s.find(id) == s.end()) {
				s.insert(id);
				nod.id = id;
				nod.fa = fa;
				q.push(nod);
			}
		}
	}
}

int main() {
	bfs();
	return 0;
}

输出:

序号=0 70 60 50 40 30 20 90 0 50 60 70 90 20 30 40 跳转到序号0

序号=1 50 60 70 40 30 20 90 1 70 60 50 90 20 30 40 跳转到序号0

序号=2 70 90 20 40 30 20 90 1 70 60 50 30 40 50 60 跳转到序号0

序号=3 90 20 30 40 40 30 20 90 1 70 60 50 50 60 70 跳转到序号0

序号=4 30 40 50 60 40 30 20 90 1 70 60 50 70 90 20 跳转到序号0

序号=5 50 60 70 20 90 70 1 60 50 40 30 90 20 30 40 跳转到序号0

序号=6 70 90 20 20 90 70 1 60 50 40 30 30 40 50 60 跳转到序号0

序号=7 90 20 30 40 20 90 70 1 60 50 40 30 50 60 70 跳转到序号0

序号=8 30 40 50 60 20 90 70 1 60 50 40 30 70 90 20 跳转到序号0

序号=9 50 60 70 70 60 50 1 40 30 20 90 90 20 30 40 跳转到序号0

序号=10 70 90 20 70 60 50 1 40 30 20 90 30 40 50 60 跳转到序号0

。。。。。。

序号=646 90 50 40 50 20 30 60 20 1 40 70 70 90 30 60 跳转到序号612

序号=647 90 30 60 50 20 30 60 20 1 40 70 70 90 50 40 跳转到序号612

序号=648 20 50 40 70 30 60 30 60 1 90 70 20 40 90 50 跳转到序号617

序号=649 40 90 50 30 60 30 60 1 90 70 20 20 50 40 70 跳转到序号617

序号=650 20 50 40 70 90 70 20 1 30 60 30 60 40 90 50 跳转到序号617

序号=651 40 90 50 90 70 20 1 30 60 30 60 20 50 40 70 跳转到序号617

启发式策略(单节点)(V2)

cpp 复制代码
//通用的数据缓存去重编号方案
template<typename T>
class GetSingleId
{
public:
	int id(T x) //所有元素按照首次传入顺序编号0,1,2......
	{
		auto it = m.find(x);
		if (it != m.end())return it->second;
		mr[n] = x;
		return m[x] = n++;
	}
	int num() //id()==num()-1表示是新元素,否则是重复元素
	{
		return n;
	}
	T getData(int id) //根据id获取元素数据
	{
		return mr[id];
	}
private:
	map<T, int>m;
	map<int, T>mr;
	int n = 0;
};

struct Node {
	vector<int>up{ 70,60,50,40,30,20,90 };
	int mid = 0;
	vector<int>down{ 50,60,70,90,20,30,40 };
	int ans = 0;
	int id = 0;
	int fa = 0;
};

int getId(vector<int> v) {
	static map< vector<int>, int>m;
	if (m.find(v) != m.end())return m[v];
	int ans = m.size();
	int len = v.size();
	while (len--) {
		v.push_back(v[0]);
		v.erase(v.begin());
		m[v] = ans;
	}
	return ans;
}
int getId(const Node& nod)
{
	static GetSingleId<vector<int>> opt;
	int id1 = getId(nod.up);
	int id2 = getId(nod.down);
	return opt.id(vector<int>{id1, nod.mid, id2});
}

vector<int> findId(const vector<int>& v)
{
	vector<int>ans;
	for (int i = 0; i < v.size(); i++) {
		int s = 0;
		for (int j = i; j < i + v.size(); j++) {
			s += v[j % v.size()];
			if (s >= 180)break;
		}
		if (s == 180)ans.push_back(i);
	}
	return ans;
}

vector<Node> getNext(const Node& nod)
{
	vector<int> id1 = findId(nod.up);
	vector<int> id2 = findId(nod.down);
	vector<Node> ans;
	for (int i : id1) {
		for (int j : id2) {
			vector<int>v1, v2;
			int k1 = i, k2 = j, s1 = 0, s2 = 0;
			while (s1 < 180) {
				v1.push_back(nod.down[k2]);
				s1 += nod.down[k2];
				k2 = (k2 + 1) % nod.down.size();
			}
			while (s2 < 180) {
				v2.push_back(nod.up[k1]);
				s2 += nod.up[k1];
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k1 != i) {
				v1.push_back(nod.up[k1]);
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k2 != j) {
				v2.push_back(nod.down[k2]);
				k2 = (k2 + 1) % nod.down.size();
			}
			ans.push_back({ v1,1 - nod.mid,v2,nod.ans + 1,0,0 });
		}
	}
	return ans;
}

//假设对半翻转操作是长度为1,另外2个操作是长度为0,对整个图进行bfs搜出单源最短路径
vector<int> bfs(vector<Node>&vn)
{
	vector<int> ans(652);
	Node nod;
	set<int>s;
	s.insert(getId(nod));
	queue<Node>q;
	q.push(nod);
	while (!q.empty()) {
		auto nod = q.front();
		int fa = nod.id;
		int newAns = nod.ans + 1;
		ans[fa] = nod.fa;
		vn.push_back(nod);
		q.pop();
		auto v = getNext(nod);
		for (auto nod : v) {
			int id = getId(nod);
			if (s.find(id) == s.end()) {
				s.insert(id);
				nod.id = id;
				nod.fa = fa;
				nod.ans = newAns;
				q.push(nod);
			}
		}
	}
	return ans;
}

bool check56(const vector<int>& v)
{
	int s50 = 0, s60 = 0;
	for (auto x : v) {
		if (x == 50)s50++;
		if (x == 60)s60++;
	}
	if (s50 != s60)return false;
	for (int i = 0; i < v.size(); i++) {
		if (v[i] != 50 && v[i] != 60)continue;
		if (v[(i + 1) % v.size()] != 110 - v[i] && v[(i + v.size() - 1) % v.size()] != 110 - v[i])return false;
	}
	return true;
}
bool check56(const Node& nod)
{
	return check56(nod.up) && check56(nod.down);
}

int getLen(vector<Node>& vn, vector<int>& fa, int id)
{
	int s = 0;
	while (!check56(vn[id])) {
		s++;
		id = fa[id];
	}
	return s;
}

int main() {
	vector<Node> vn;
	vector<int> fa = bfs(vn);
	for (int i = 0; i < fa.size(); i++) {
		cout << i << " " << fa[i] << endl;
	}
	int max1 = 0, max2 = 0, maxs = 0;
	for (int i = 0; i < fa.size(); i++) {
		int x = getLen(vn, fa, i);
		max1 = max(max1, x);
		max2 = max(max2, vn[i].ans - x);
		maxs = max(maxs, vn[i].ans);
	}
	cout << endl << "max:" << max1 << " " << max2 << " " << maxs;
	return 0;
}

输出:

。。。。。。

643 609

644 612

645 612

646 612

647 612

648 617

649 617

650 617

651 617

max:10 6 13

所有状态的最速复原都可以分为2步,第一步是完成2组50+60的合并,第二步是彻底复原。其中的第一步,最多需要10次大操作,其中的第二步,最多需要6次大操作,而总共最多需要13次大操作。

启发式策略(双节点)(V2)

cpp 复制代码
//通用的数据缓存去重编号方案
template<typename T>
class GetSingleId
{
public:
	int id(T x) //所有元素按照首次传入顺序编号0,1,2......
	{
		auto it = m.find(x);
		if (it != m.end())return it->second;
		mr[n] = x;
		return m[x] = n++;
	}
	int num() //id()==num()-1表示是新元素,否则是重复元素
	{
		return n;
	}
	T getData(int id) //根据id获取元素数据
	{
		return mr[id];
	}
private:
	map<T, int>m;
	map<int, T>mr;
	int n = 0;
};

struct Node {
	vector<int>up{ 70,60,50,40,30,20,90 };
	int mid = 0;
	vector<int>down{ 50,60,70,90,20,30,40 };
	int ans = 0;
	int id = 0;
	int fa = 0;
};

int getId(vector<int> v) {
	static map< vector<int>, int>m;
	if (m.find(v) != m.end())return m[v];
	int ans = m.size();
	int len = v.size();
	while (len--) {
		v.push_back(v[0]);
		v.erase(v.begin());
		m[v] = ans;
	}
	return ans;
}
int getId(const Node& nod)
{
	static GetSingleId<vector<int>> opt;
	int id1 = getId(nod.up);
	int id2 = getId(nod.down);
	return opt.id(vector<int>{id1, nod.mid, id2});
}

vector<int> findId(const vector<int>& v)
{
	vector<int>ans;
	for (int i = 0; i < v.size(); i++) {
		int s = 0;
		for (int j = i; j < i + v.size(); j++) {
			s += v[j % v.size()];
			if (s >= 180)break;
		}
		if (s == 180)ans.push_back(i);
	}
	return ans;
}

vector<Node> getNext(const Node& nod)
{
	vector<int> id1 = findId(nod.up);
	vector<int> id2 = findId(nod.down);
	vector<Node> ans;
	for (int i : id1) {
		for (int j : id2) {
			vector<int>v1, v2;
			int k1 = i, k2 = j, s1 = 0, s2 = 0;
			while (s1 < 180) {
				v1.push_back(nod.down[k2]);
				s1 += nod.down[k2];
				k2 = (k2 + 1) % nod.down.size();
			}
			while (s2 < 180) {
				v2.push_back(nod.up[k1]);
				s2 += nod.up[k1];
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k1 != i) {
				v1.push_back(nod.up[k1]);
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k2 != j) {
				v2.push_back(nod.down[k2]);
				k2 = (k2 + 1) % nod.down.size();
			}
			ans.push_back({ v1,1 - nod.mid,v2,nod.ans + 1,0,0 });
		}
	}
	return ans;
}

//假设对半翻转操作是长度为1,另外2个操作是长度为0,对整个图进行bfs搜出单源最短路径
vector<int> bfs(vector<Node>&vn)
{
	vector<int> ans(652);
	Node nod;
	set<int>s;
	s.insert(getId(nod));
	queue<Node>q;
	q.push(nod);
	while (!q.empty()) {
		auto nod = q.front();
		int fa = nod.id;
		int newAns = nod.ans + 1;
		ans[fa] = nod.fa;
		vn.push_back(nod);
		q.pop();
		auto v = getNext(nod);
		for (auto nod : v) {
			int id = getId(nod);
			if (s.find(id) == s.end()) {
				s.insert(id);
				nod.id = id;
				nod.fa = fa;
				nod.ans = newAns;
				q.push(nod);
			}
		}
	}
	return ans;
}

bool check5656(const vector<int>& v)
{
	int s50 = 0, s60 = 0;
	for (auto x : v) {
		if (x == 50)s50++;
		if (x == 60)s60++;
	}
	if (s50 != s60)return false;
	for (int i = 0; i < v.size(); i++) {
		if (v[i] != 50 && v[i] != 60)continue;
		if (v[(i + 1) % v.size()] != 110 - v[i] && v[(i + v.size() - 1) % v.size()] != 110 - v[i])return false;
	}
	return true;
}
bool check5656(const Node& nod)
{
	return check5656(nod.up) && check5656(nod.down);
}
bool check56(const vector<int>& v)
{
	for (int i = 0; i < v.size(); i++) {
		if (v[i] != 50 && v[i] != 60)continue;
		if (v[(i + 1) % v.size()] == 110 - v[i])return true;
	}
	return false;
}
bool check56(const Node& nod)
{
	return check56(nod.up) || check56(nod.down);
}

void getLen(vector<Node>& vn, vector<int>& fa, int id, int &len1, int &len2)
{
	int id0 = id;
	len1 = 0, len2 = 0;
	while (!check56(vn[id])) {
		len1++;
		id = fa[id];
	}
	while (!check5656(vn[id])) {
		len2++;
		id = fa[id];
	}
	cout << id0 << " " << len1 << " " << len2 << " " << vn[id0].ans - len1 - len2 << endl;
}

int main() {
	vector<Node> vn;
	vector<int> fa = bfs(vn);
	for (int i = 0; i < fa.size(); i++) {
		cout << i << " " << fa[i] << endl;
	}
	int max1 = 0, max2 = 0, max3 = 0, maxs = 0;
	for (int i = 0; i < fa.size(); i++) {
		int len1, len2;
		getLen(vn, fa, i, len1, len2);
		max1 = max(max1, len1);
		max2 = max(max2, len2);
		max3 = max(max3, vn[i].ans - len1 - len2);
		maxs = max(maxs, vn[i].ans);
	}
	cout << endl << "max:" << max1 << " " << max2 << " " << max3 << " " << maxs;
	return 0;
}

输出:

。。。。。。

644 0 10 3

645 7 3 3

646 7 3 3

647 0 10 3

648 7 3 3

649 7 3 3

650 7 3 3

651 7 3 3

max:7 10 6 13

其中,只有8种情况是中间为10的

直接挑出来:

序号=620 90 50 40 40 70 70 0 50 20 30 60 20 90 30 60 跳转到序号588

序号=623 90 30 60 50 20 30 60 20 0 40 70 70 90 50 40 跳转到序号588

序号=633 20 60 30 20 50 60 30 90 0 40 50 90 70 70 40 跳转到序号601

序号=634 70 70 40 40 50 90 0 60 30 90 20 60 30 20 50 跳转到序号601

序号=641 20 60 30 20 50 60 30 90 1 40 50 90 70 70 40 跳转到序号609

序号=642 70 70 40 40 50 90 1 60 30 90 20 60 30 20 50 跳转到序号609

序号=644 90 50 40 40 70 70 1 50 20 30 60 20 90 30 60 跳转到序号612

序号=647 90 30 60 50 20 30 60 20 1 40 70 70 90 50 40 跳转到序号612

显然,这8种情况只需要合适去重之后就会变成一种情况,但是我们不需要再去重了。

根据这8种情况,提取新的特征:70 70 90挨着了。

启发式策略(三节点)(最终策略)

cpp 复制代码
//通用的数据缓存去重编号方案
template<typename T>
class GetSingleId
{
public:
	int id(T x) //所有元素按照首次传入顺序编号0,1,2......
	{
		auto it = m.find(x);
		if (it != m.end())return it->second;
		mr[n] = x;
		return m[x] = n++;
	}
	int num() //id()==num()-1表示是新元素,否则是重复元素
	{
		return n;
	}
	T getData(int id) //根据id获取元素数据
	{
		return mr[id];
	}
private:
	map<T, int>m;
	map<int, T>mr;
	int n = 0;
};

struct Node {
	vector<int>up{ 70,60,50,40,30,20,90 };
	int mid = 0;
	vector<int>down{ 50,60,70,90,20,30,40 };
	int ans = 0;
	int id = 0;
	int fa = 0;
};

int getId(vector<int> v) {
	static map< vector<int>, int>m;
	if (m.find(v) != m.end())return m[v];
	int ans = m.size();
	int len = v.size();
	while (len--) {
		v.push_back(v[0]);
		v.erase(v.begin());
		m[v] = ans;
	}
	return ans;
}
int getId(const Node& nod)
{
	static GetSingleId<vector<int>> opt;
	int id1 = getId(nod.up);
	int id2 = getId(nod.down);
	return opt.id(vector<int>{id1, nod.mid, id2});
}

vector<int> findId(const vector<int>& v)
{
	vector<int>ans;
	for (int i = 0; i < v.size(); i++) {
		int s = 0;
		for (int j = i; j < i + v.size(); j++) {
			s += v[j % v.size()];
			if (s >= 180)break;
		}
		if (s == 180)ans.push_back(i);
	}
	return ans;
}

vector<Node> getNext(const Node& nod)
{
	vector<int> id1 = findId(nod.up);
	vector<int> id2 = findId(nod.down);
	vector<Node> ans;
	for (int i : id1) {
		for (int j : id2) {
			vector<int>v1, v2;
			int k1 = i, k2 = j, s1 = 0, s2 = 0;
			while (s1 < 180) {
				v1.push_back(nod.down[k2]);
				s1 += nod.down[k2];
				k2 = (k2 + 1) % nod.down.size();
			}
			while (s2 < 180) {
				v2.push_back(nod.up[k1]);
				s2 += nod.up[k1];
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k1 != i) {
				v1.push_back(nod.up[k1]);
				k1 = (k1 + 1) % nod.up.size();
			}
			while (k2 != j) {
				v2.push_back(nod.down[k2]);
				k2 = (k2 + 1) % nod.down.size();
			}
			ans.push_back({ v1,1 - nod.mid,v2,nod.ans + 1,0,0 });
		}
	}
	return ans;
}

//假设对半翻转操作是长度为1,另外2个操作是长度为0,对整个图进行bfs搜出单源最短路径
vector<int> bfs(vector<Node>&vn)
{
	vector<int> ans(652);
	Node nod;
	set<int>s;
	s.insert(getId(nod));
	queue<Node>q;
	q.push(nod);
	while (!q.empty()) {
		auto nod = q.front();
		int fa = nod.id;
		int newAns = nod.ans + 1;
		ans[fa] = nod.fa;
		vn.push_back(nod);
		q.pop();
		auto v = getNext(nod);
		for (auto nod : v) {
			int id = getId(nod);
			if (s.find(id) == s.end()) {
				s.insert(id);
				nod.id = id;
				nod.fa = fa;
				nod.ans = newAns;
				q.push(nod);
			}
		}
	}
	return ans;
}

bool check5656(const vector<int>& v)
{
	int s50 = 0, s60 = 0;
	for (auto x : v) {
		if (x == 50)s50++;
		if (x == 60)s60++;
	}
	if (s50 != s60)return false;
	for (int i = 0; i < v.size(); i++) {
		if (v[i] != 50 && v[i] != 60)continue;
		if (v[(i + 1) % v.size()] != 110 - v[i] && v[(i + v.size() - 1) % v.size()] != 110 - v[i])return false;
	}
	return true;
}
bool check5656(const Node& nod)
{
	return check5656(nod.up) && check5656(nod.down);
}
bool check56(const vector<int>& v)
{
	for (int i = 0; i < v.size(); i++) {
		if (v[i] != 50 && v[i] != 60)continue;
		if (v[(i + 1) % v.size()] == 110 - v[i])return true;
	}
	return false;
}
bool check56(const Node& nod)
{
	return check56(nod.up) || check56(nod.down);
}
bool check779(const vector<int>& v)
{
	for (int i = 0; i < v.size(); i++) {
		if (v[i] != 90)continue;
		if (v[(i + 1) % v.size()] == 70 && v[(i + 2) % v.size()] == 70)return false;
		if (v[(i + v.size() - 1) % v.size()] == 70 && v[(i + v.size() - 2) % v.size()] == 70)return false;
	}
	return true;
}
bool check779(const Node& nod)
{
	return check779(nod.up) && check779(nod.down);
}

void getLen(vector<Node>& vn, vector<int>& fa, int id, int &len1, int &len2, int &len3)
{
	int id0 = id;
	len1 = 0, len2 = 0, len3 = 0;
	while (!check779(vn[id])) {
		len1++;
		id = fa[id];
	}
	while (!check56(vn[id])) {
		len2++;
		id = fa[id];
	}
	while (!check5656(vn[id])) {
		len3++;
		id = fa[id];
	}
	cout << id0 << " " << len1 << " " << len2 << " " <<len3<<" "<< vn[id0].ans - len1 - len2 -len3<< endl;
}

int main() {
	vector<Node> vn;
	vector<int> fa = bfs(vn);
	for (int i = 0; i < fa.size(); i++) {
		cout << i << " " << fa[i] << endl;
	}
	int max1 = 0, max2 = 0, max3 = 0, max4=0, maxs = 0;
	for (int i = 0; i < fa.size(); i++) {
		int len1, len2, len3;
		getLen(vn, fa, i, len1, len2,len3);
		max1 = max(max1, len1);
		max2 = max(max2, len2);
		max3 = max(max3, len3);
		max4 = max(max4, vn[i].ans - len1 - len2-len3);
		maxs = max(maxs, vn[i].ans);
	}
	cout << endl << "max:" << max1 << " " << max2 << " " << max3 << " " << max4 <<" "<< maxs;
	return 0;
}

输出:

。。。。。。

640 1 6 3 3

641 1 6 3 3

642 1 6 3 3

643 1 6 3 3

644 1 6 3 3

645 1 6 3 3

646 1 6 3 3

647 1 6 3 3

648 0 7 3 3

649 0 7 3 3

650 0 7 3 3

651 0 7 3 3

max:1 7 4 6 13

所有状态的最速复原都可以分为4步,第一步是把70+70+90这个组合给拆开,第二步是完成1组50+60的合并,第三步是完成2组50+60的合并,第四步是彻底复原。

这四步分别最多需要1次、7次、4次、6次大操作,而总共最多需要13次大操作。

{1 7 4 6}这几个数字已经比较小了,所以具备了实际可用的启发功能。

亲测好用,至此,研究工作告一段落。

相关推荐
艾莉丝努力练剑18 小时前
【优选算法必刷100题:专题五】(位运算算法)第033~38题:判断字符是否唯一、丢失的数字、两整数之和、只出现一次的数字 II、消失的两个数字
java·大数据·运维·c++·人工智能·算法·位运算
光羽隹衡18 小时前
机器学习——DBSCAN算法
人工智能·算法·机器学习
vyuvyucd18 小时前
Java数组与Arrays类实战指南
数据结构·算法
逝川长叹18 小时前
利用 SSI-COV 算法自动识别线状结构在环境振动下的模态参数研究(Matlab代码实现)
前端·算法·支持向量机·matlab
山上三树18 小时前
详细介绍 C 语言中的匿名结构体
c语言·开发语言·算法
EXtreme3518 小时前
【数据结构】彻底搞懂二叉树:四种遍历逻辑、经典OJ题与递归性能全解析
c语言·数据结构·算法·二叉树·递归
炽烈小老头18 小时前
【每天学习一点算法 2026/01/09】3的幂
学习·算法
渡我白衣18 小时前
计算机组成原理(14):算术逻辑单元ALU
大数据·人工智能·算法·机器学习·计组·数电·alu
柳鲲鹏18 小时前
OpenCV视频实时跟踪目标,多种算法,python版
opencv·算法·音视频