整體趨於無窮大,部分趨於無窮小。按照“無有即無窮,無窮即無有”的哲學(參考《算法之道》),因此其於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;
}