1+1到底2不2?

本文介绍了动态规划的基本原理及其应用实例,包括费用问题和数字串乘法问题,并探讨了A*算法在图搜索中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于面试被人告知要多了解些算法知识,最近学习一点算法知识,只是皮毛。
整体趋于无穷大,部分趋于无穷小。按照“无有即无穷,无穷即无有”的哲学(参考《算法之道》),因此其于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;
}



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值