1+1到底2不2?

由於面試被人告知要多瞭解些算法知識,最近學習一點算法知識,只是皮毛。
整體趨於無窮大,部分趨於無窮小。按照“無有即無窮,無窮即無有”的哲學(參考《算法之道》),因此其於0無差別,也證明是可以相互轉化的,在轉化過程中,我們要找到“守恆”條件,處理好細節問題,實現整體。歸納之整體到部分的分析過程,再到部分到整體的實現過程。
下面:動態規劃和圖搜索(1)動態規劃
將待求解問題分解成若干子問題;
最優子結構:最優策略的子策略也是最優策略(不互相獨立)
重疊子問題:每次產生的子問題並不總是新問題
從子問題的解得到原問題的解。
例1:費用問題
爲一個乘遊船去某個景點的遊客計算最少租船策略。
輸入:測試數據第一行n表示起點站0到後面站的路徑數,接下來是初始點0到n個站得代價,再下來的是第一個站點到剩餘n-1個站點的租金,依此類推。
例如輸入
3
2 3 6
1 3
2
3
4 7 9
4 5
6
輸出
Case1 :5
Case2:9
/************************************************************************/
/* 設m[i]起點站到第i站得最少遊船租金
/* 第k站中轉到第i站
/* 狀態轉移方程 m[i] = min{m[k] + r[k][i]}     
/* @Bean
/************************************************************************/
#include <iostream>
using namespace std;
const int MAXNUM = 256;
int n, r[MAXNUM][MAXNUM], m[MAXNUM];

void money(int rr[][MAXNUM], int mm[], int nn)
{
 for (int i = 1; i <= nn; i++)
 {
  m[i] = r[0][i];
  for (int j = i - 1; j > 0; j --)
  {
   if (m[i] > m[j] + rr[j][i])
   {
    m[i] = m[j] + rr[j][i];
   }
  }
 }
}

int main()
{
 int num = 0, a;
 while (cin >> n)
 {
  if (n == 0)
  {
   break;
  }
  else
  {
   num ++;
   for (int i = 0; i < n; i++)
   {
    for (int j = i + 1; j <= n; j++)
    {
     cin >> a;
     r[i][j] = a;
    }
   }
   money(r, m, n);
   cout << "Case" << num << ":" << endl;
   cout << m[n] << endl;
  }
 }
 system("pause");
 return 0;
}


例2:有一個數字串312,有兩種分法,3*12 = 36和31*2=62得的乘數最大
輸入:
第一行2個自然數N,K(6<=N<=40, 1<=K<=6 )
第二行是長度N的數字串

/************************************************************************/
/* F[i][k]是長度i+1插入k個×
/* 狀態轉移方程 F[i][k] = max{F[t][k-1]*num[t+1][i]}   
/* @Bean
/************************************************************************/
#include <iostream>
#include <cstring>
using namespace std;

int const k_max_num = 41;
int num[k_max_num], len;

// num[t...n] 整數合成
long nn(int t, int z)
{
 int i;
 long a = num[t];
 for (i = t + 1; i <= z; i++)
 {
  a = a * 10 + num[i];
 }
 return a;
}

int main()
{
 int i, N, c, k, t;
 long n, F[k_max_num][k_max_num];
 char s[k_max_num];
 // c 即題目的 K
 while (cin >> N >> c)
 {
  cin >> s;
  len = strlen(s);
  for (i = 0; i < len; i++)
  {
   num[i] = s[i] - 48; // 字符串轉換爲整型
  }
  k = 0;
  F[0][0] = num[0];
  // num[0...i]整數合成
  for (i = 1; i < len; i++)
  {
   F[i][0] = F[i-1][0] * 10 + num[i];
  }
  k = 1;
  // 劃分c+1個數字
  for (k = 1; k <= c; k++)
  {
   for(i = k; i < len; i++)
   {
    long a = -1;
    for (t = k - 1; t < i; t++)
    {
     n = nn(t+1, i);
     // (多位數)整數乘法
     long b = F[t][k-1] * n;
     if (a < b) a = b;
    }
    F[i][k] = a;
   }
  }
  cout << F[len -1][c] << endl;
 }
 return 0;
}


 

(2) A*算法void AStar() // 最小值
{
CLOSED與OPEN初始化爲空;
將起始節點放入OPEN表;
while(open!=NULL)
{
  從OPEN表中取估價值f最小的節點CurrentNode;
  將CurrentNode節點從OPEN表中刪除;
  if(CurrentNode節點==目標節點)
  {
   求得路徑PATH;break;
  }else
  {
   for (CurrentNode節點的每一個子節點NewNode)
   {
    if(NewNode不在OPEN表和CLOSED表中)
    {
     求NewNode的估價值f;
     將NewNode插入OPEN表中;
    }else if (NewNode在OPEN表中)
    {
     if(NewNode的估價值f小於OPEN表中節點的估價值)
      更新OPEN表中的估價值; // 取最小路徑的估價值
    }else // NewNode在CLOSED表中
    {
if (NewNode的估價值f小於CLOSED表中節點估價值)
     {
      更新CLOSED表中的估價值;
      從CLOSED表中移除節點,並把NewNode放入OPEN表;
     }
    }
    將CurrentNode節點插入CLOSED表中;
    按照估價值將OPEN表中的節點排序;
   }
  }
}
}

八數碼問題:

#include <iostream>
#include <vector>
using namespace std;
int dist[9][9];
typedef struct s_node
{
 struct s_node *father;
 struct s_node *prev;
 struct s_node *next;
 int step; // 已有步數
 int diff; // 節點估計函數值
 int weight; // 節點的估價函數值weight = step = diff
 int m[9]; // m[i]的取值0-8,0表示空格
}node;

void init() // 初始化,計算哈密頓距離列表
{
 int i, j, posi[9][2], dist[9][9];
 posi[0][0] = 2;
 posi[0][1] = 2;
 for (i = 1; i < 9; ++i)
 {
  posi[i][0] = (i-1)/3;
  posi[i][1] = (i-1)%3;
 }
 for (j = 0; j < 9; ++j)
 {
  for (i = 0; i < 9; ++i)
  {
   dist[j][i] = abs(posi[i][0] - posi[j][0]) + abs(posi[i][1] - posi[j][1]);
  }
 }
}

node* open_out(node *head) // 取頭結點出列
{
 node *p;
 if (head->next == NULL)
 {
  return NULL;
 }
 p = head->next;
 head->next = p->next;
 if (p->next != NULL)
 {
  p->next->prev = head;
 }
 p->prev = NULL;
 p->next = NULL;
 return p;
}

int open_insert(node *head, node *item)
{
 node *p, *q;
 p = head->next;
 q = head;
 while (p != NULL && item->weight > p->weight)
 {
  q = p;
  p = p->next;
 }
 q->next = item;
 item->prev = q;
 item->next = p;
 if (p!=NULL)
 {
  p->prev = item;
 }
 return 0;
}

int close_insert(node *head, node *item) //將item插入close的頭部
{
 item->next = head->next;
 item->prev = head;
 head->next = item;
 if (item->next != NULL)
 {
  item->next->prev = item;
 }
 return 0;
}

int inv_pair(int m[]) // 計算八數碼棋盤的逆序對數
{
 int i, j, total = 0;
 for (i = 1; i < 9; ++i)
 {
  for (j = 0; j < i; ++j)
  {
   if (m[i] != 0 && m[j] != 0 && m[j] < m[i])
   {
    ++total;
   }
  }
 }
 return total;
}

void swap(int *a, int *b) // 棋盤符交換
{
 int c;
 c = *a;
 *a = *b;
 *b = c;
}

int operate(int m[], int op)
{
 int black = 0;
 while (m[black] != 0 && black < 9)
 {
  ++black;
 }
 if (black == 9)
 {
  return 1;
 }
 switch (op)
 {
 case 1:// 上
  if (black > 2)
  {
   // 不在第一行
   swap(m + black, m + black -3);
  }
  break;
 case 2: // 下
  if (black < 6)
  {
   // 不在第三行
   swap(m + black, m + black + 3);
  }
  break;
 case 3: // 左
  if (black != 0 && black != 3 && black != 6)
  {
   // 不在第一列
   swap(m + black, m + black -1);    
  }
  break;
 case 4: // 右
  if (black != 2 && black != 5 && black != 8)
  {
   swap(m + black, m + black + 1);
  }
  break;
 default:
  return 1;
 }
 return 0;
}

 int diff(int m[], int n[]) // 採用不同位置的個數進行估值--估值方法一
 {
  int i, d = 0;
  for (i = 0; i < 9; i++)
  {
   if (m[i] != n[i])
   {
    ++d;
   }
  }
  return d;

 }

 int eval(int m[]) // 對0-8數碼進行Hamilton距離進行估值--估值方法二
 {
  int i, val = 0;
  for (i = 0; i < 9; i++)
  {
   val += dist[i][m[i]];
  }
  return val;
 }

void input_m(int m[]) // 初始八數碼棋盤輸入
{
 char str[10];
 cin.getline(str, 10);
 for (int i = 0; i < 9; i++)
 {
  m[i] = str[i] - 48;
 }
}

// 檢查棋盤m在OPEN和CLOSED中是否出現,如找到返回1,否則返回0
int new_m(node *open, node *close, int m[])
{
 node *p;
 int same, i;
 p = close->next;
 while (p != NULL) // 先在closed中查找
 {
  same = 1;
  for (i = 0; i < 9; ++i)
  {
   if (p->m[i] != m[i])
   {
    same  = 0;
   }
  }
  if (same) return 0;
  p = p->next;
 }
 p = open->next;
 while (p != NULL)
 {
  same = 1;
  for (i = 0; i < 9; ++i)
  {
   if (p->m[i] != m[i])
   {
    same = 0;
   }
//   if (same) return 0;
  }
  if (same) return 0;
  p = p->next;
 }
 return 1;
}

// 拷貝節點
node* copy_node(node *origin)
{
 node *p;
 int i;
 p = new node;
 p->step = origin->step;
 p->diff = origin->diff;
 p->weight = origin->weight;
 for (i = 0; i < 9; ++i)
  (p->m)[i] = (origin->m)[i];
 return p;
}

// 從後向前計算移動步數
int print_result(node *item)
{
 node *p;
 int step;
 p = item;
 if (p != NULL)
 {
  step = print_result(p->father);
  return step + 1;
 }
 else return -1;
}

// 刪除鏈表
void free_queue(node *head)
{
 node *p, *q;
 p = head->next;
 while (p != NULL)
 {
  q = p->next;
  delete p;
  p = q;
 }
 delete head;
}

int main()
{
 node *open, *closed;
 node *p1, *p2;
 int op, node_num, total_step, n, i;
 int final_m[9] = {1, 2, 3, 4, 5, 6, 7, 8, 0}; // 目標狀態
 bool flg;
 init();
 cin >> n;
 cin.get();
 for (i = 0; i < n; i++)
 {
  cout << "Case" << i + 1 << ":" << endl;
  flg = false;
  open = new node;
  closed = new node;
  open->prev = closed->prev = open->next = closed->next = NULL;
  p1 = new node;
  p1->father = NULL;
  p1->step = 0;
  input_m(p1->m);
  if (diff(p1->m, final_m) == 0) //如果初始棋盤就是目標棋盤
  {
   cout << 0 << endl;
   continue;
  }
  else
  {
   open_insert(open, p1); // 將初始棋盤信息存入open中
   if ((inv_pair(p1->m)%2) != (inv_pair(final_m)%2)) // 檢查是否有解
   {
    cout << "Impossible!\n";
    continue;
   }
   node_num = 1;
   p1 = open_out(open); // 從open表中取出節點
   while (p1 != NULL)
   {
    close_insert(closed, p1); // 將節點存入closed中
    // generate new node
    // 對當前棋盤進行上下左右嘗試
    for (op = 1; op <= 4; ++op)
    {
     p2 = copy_node(p1);
     operate(p2->m, op); // 得到新棋盤狀態p2
     // 檢查p2是否在open或closed表中出現
     if (new_m(open, closed, p2->m))
     {
      p2->father = p1;
      p2->step = p1->step + 1;
      p2->diff = eval(p2->m); // 求新節點的啓發函數值
      p2->weight = p2->step + p2->diff; // 求新節點估價函數值
      if (diff(p2->m, final_m) == 0) // 找到解
      {
        total_step = print_result(p2); // 計算移動步數
        cout << total_step << "\n";
        free_queue(open);
        free_queue(closed);
        flg = true;
        break;
      }
      else
      {
       // 如p2不是目標狀態,插入open表
       open_insert(open, p2);
      }
     }
     else
      delete p2;
    }
    if (flg) break;
    p1 = open_out(open);
   }
  }
 }
 system("pause");
 return 0;
}



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章