要滿足兩個條件:
1.封閉的環
2.是一個連通圖,且圖中任意兩點可達
經過圖(有向圖或無向圖)中所有頂點一次且僅一次的通路稱爲哈密頓通路。
經過圖中所有頂點一次且僅一次的迴路稱爲哈密頓迴路。
具有哈密頓迴路的圖稱爲哈密頓圖,具有哈密頓通路但不具有哈密頓迴路的圖稱爲半哈密頓圖。
平凡圖是哈密頓圖。
問題描述
給你一個整數m,找出這樣一個長爲2^m的0,1序列,使得依次取長爲m的串時,得到的2^m個長爲m的0,1串,它們表示了互不相同的十進制數。
輸入:第一行是一個整數n,表示需考察n個整數(1<=n<=15),接着n行,每行有一個整數m(1<=m<=15)
樣例:
2
2
3
m=2: 0011
m=3: 00010111
考察m=2的情形,長爲2的序列00開始,每個序列後面接0或1,於是接0得000,前面兩位00和後面兩位的00相同,應排除本次接0的情況,應接1,變成001,後01與前00不同,應保留填上1.確定01後,對01後面接0或1,應該有010和011,都可行,取低2位10或11,類似接0或1.最後得出下面有向圖。
#include <iostream>
using namespace std;
#define MAXN 65535
struct node
{
int left, right; // left,right分別記錄當前節點通過添0,添1而得的序列
int visited; // 標記是否被訪問到,-1表示未訪問過,0表示左支,1表示右支
};
typedef node NODE;
NODE p[MAXN]; // 表示節點數組
long maxb, a[MAXN]; // a存放最優解
int m;
void init(int m) // 初始化構建一個有向圖
{
long i, k;
maxb = (1<<m) - 1; // 記有向圖的頂點最大頂點數-1
for (i = 0; i <= maxb; i++)
{
// 設定無左右兒子,無訪問值
p[i].left = -1;
p[i].right = -1;
p[i].visited = -1;
k = i; // 考察當前十進制數是i的節點的左右兒子生成情況
k = (k << 1) & maxb; // 節點的二進制數尾添0,用位操作,取後m位
if (k != i)
{
p[i].left = k; // 去左支重複節點
}
k = k + 1; // 嘗試右支
if (k != i)
{
p[i].right = k; // 去右支重複節點
}
}
}
// 判斷數b與當前部分解a前k個是否有相同的
bool NotEqual(int k, int b)
{
bool flag = true;
int i;
for (i = 0; i <= k; i++)
{
if (a[i] == b)
{
flag = false;
break;
}
}
return flag;
}
// 求最優解a,採用迭代回溯算法
void Compt()
{
long i = 1, j;
bool flag = false;
a[0] = 0;
p[0].visited = 0;
while (true) // 搜索子樹
{
while (i <= maxb && p[a[i-1]].left != -1 && p[a[i-1]].left != 0
&& NotEqual(i-1, p[a[i-1]].left))
{// 如左子樹一直可行,沿左子樹一直下去
a[i] = p[a[i-1]].left; //取左兒子值
p[a[i-1]].visited = 0; //設訪問左兒子標誌
i++;
flag = false; // 設定可能進入右子樹標誌
}
// 如果個數已經夠了,直接輸出結果
if (i > maxb)
{
for (j = 0; j <= maxb; j++)
{
cout << ((a[j]&(1<<(m-1)))>>(m-1)); // 按字符串輸出
}
cout << endl;
return;
}
else // 如果個數未滿足要求
{
// 若右子樹可行進入右子樹
if (p[a[i-1]].right != -1 && p[a[i-1]].right != 0 && NotEqual(i-1, p[a[i-1]].right))
{
a[i] = p[a[i-1]].right; //取右兒子值
p[a[i-1]].visited = 0; //設訪問右兒子標誌
i++;
flag = false; // 設定可能進入左子樹標誌
}
}
while (!flag) // 剪枝回溯
{
i--;
while (i > 0 && p[a[i-1]].visited == 1) // 沿右子樹返回
{
a[i] = -1;
p[a[i-1]].visited = -1; // 取消訪問標誌
i--;
}
if (p[a[i-1]].right != -1 && p[a[i-1]].right != 0 && NotEqual(i-1, p[a[i-1]].right))
{
a[i] = p[a[i-1]].right; //取右兒子值
p[a[i-1]].visited = 1; //設訪問右兒子標誌
i++;
flag = true; // 設定可能進入左子樹標誌
}
}
}
}
int main()
{
int i, n;
cin >> n;
for (i = 0; i < n; i++)
{
cin >> m;
cout << "m=" << m << ":" << endl;
if (m <= 0)
{
cout << "Impossible!" << endl;
}
else
{
init(m);
Compt();
}
}
system("pause");
return 0;
}