一、问题描述
二、问题分析
一开始,我使用了贪心的方式(也在C/C++实现中,是错的),认为短视能够获得好的结果,运行结果确实是13步最少,但是路径却不是数组路径,debug发现在0开始的贪心路径中,得到的最好结果是14,而书中0开始的最好结果为13,也就是说贪心算法至少在求局部最优时达不到效果;于是想到DP(也在C/C++实现中),但是对于DP来说,中间的计算结果不易保存,那么递归实现必然有大量的重复计算,而且DP的最终效果依旧是遍历所有排列,故最终还是选择了对所有排列的遍历(Python实现)。
三、代码实现
1.C/C++实现
#include <iostream>
using namespace std;
// 状态码
const int codes[10] = {
0b1111110, // 0
0b0110000, // 1
0b1101101, // 2
0b1111001, // 3
0b0110011, // 4
0b1011011, // 5
0b1011111, // 6
0b1110000, // 7
0b1111111, // 8
0b1111011 // 9
};
// 最大步数,用于初始化
const int MAX_STEPS_INIT = 7 * 10;
// 记录状态之间的距离
int distances[10][10];
// 记录已使用的标志,和顺序
int used[10];
int order[10][1 << 10];
// 计算两个状态之间的变化距离
void calc_distances() {
for (int i = 0; i < 10; i++) {
for (int j = i; j < 10; j++) {
distances[i][j] = 0;
if (i != j) {
int tmp = (codes[i] ^ codes[j]);
while (tmp) {
distances[i][j] += (tmp % 2);
tmp /= 2;
}
distances[j][i] = distances[i][j];
}
}
}
}
// 由 cur 开始进行贪心的变换数量
int greedy(int cur) {
int total = 0;
for (int i = 1; i < 11; i++) {
used[cur] = i;
// 找下一个
int next = -1, steps = 14;
for (int j = 0; j < 10; j++) {
if (used[j] == 0) {
if (distances[cur][j] < steps) {
steps = distances[cur][j];
next = j;
}
}
}
if (next >= 0) {
cur = next;
total += steps;
}
}
return total;
}
// 由 cur 开始进行dp的变换数量
int dp(int cur, int left) {
if (left == (1 << 10) - 1) {
return 0;
}
int min_steps = MAX_STEPS_INIT;
for (int i = 0; i < 10; i++) {
if ((left & (1 << i)) == 0) {
int tmp = distances[cur][i] + dp(i, left | (1 << i));
if (tmp < min_steps) {
min_steps = tmp;
order[cur][left] = i;
}
}
}
return min_steps;
}
int main() {
calc_distances();
int min_op = MAX_STEPS_INIT;
int min_route[10] = { 0 };
// 使用贪心法 (×)
for (int i = 0; i < 10; i++) {
memset(used, 0, sizeof(used));
int tmp = greedy(i);
if (tmp < min_op) {
min_op = tmp;
for (int j = 0; j < 10; j++) {
min_route[used[j] - 1] = j;
}
}
}
cout << min_op << endl;
for (int i = 0; i < 10; i++) {
cout << min_route[i] << '\t';
}
cout << endl;
// 使用 DP
min_op = MAX_STEPS_INIT;
int min_index = 0;
for (int i = 0; i < 10; i++) {
int tmp = dp(i, 1 << i);
if (tmp < min_op) {
min_op = tmp;
min_index = i;
}
}
cout << min_op << endl;
cout << min_index << '\t';
int cur = min_index, left = 1 << cur;
for (int i = 0; i < 9; i++) {
cout << order[cur][left] << '\t';
cur = order[cur][left];
left |= 1 << cur;
}
return 0;
}
2.Python实现
# coding = utf-8
from itertools import permutations
MAX_STEPS = 7 * 10
CODES = [0b1111110, # 0
0b0110000, # 1
0b1101101, # 2
0b1111001, # 3
0b0110011, # 4
0b1011011, # 5
0b1011111, # 6
0b1110000, # 7
0b1111111, # 8
0b1111011] # 9
distances = []
def calc_distances():
for i in range(10):
tmp = [0] * 10
for j in range(10):
if i != j:
tmp[j] = str(bin(CODES[i] ^ CODES[j])).count('1')
distances.append(tmp)
pass
def calc_for_route(route):
steps = 0
for i in range(len(route) - 1):
steps += distances[route[i]][route[i + 1]]
return steps
def get_min():
calc_distances()
min_steps = MAX_STEPS
min_route = ()
for item in permutations(range(10)):
tmp = calc_for_route(item)
if min_steps > tmp:
min_steps = tmp
min_route = item
return min_steps, min_route
if __name__ == '__main__':
steps, route = get_min()
print(steps, route)
pass