哈密頓迴路-相異數字序列問題

要滿足兩個條件:

  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;
}


 

發佈了70 篇原創文章 · 獲贊 18 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章