算法学习之动态规划(例题)

最长公共子序列LCS

设序列 Xi = {x1, x2, …, xi},Yj = {y1, y2, …, yj}, c(i,j) 表示Xi和Yj的LCS的长度,则有

            { 0                                     i=0 或 j=0
    c(i,j)= { c[i-1,j-1]+1                          x_i=y_j
            { max⁡{c[i,j-1],c[i-1,j]}                x_i≠y_j 

思路:
从后向前对两个序列进行判断,因为当我们从后向前进行判断的时候,只存在两种情况,即两个序列的最后元素相等,或者不相等,假如两个序列的最后一个元素相等,那么就是说最后这个元素肯定是其最长公共子序列的最后一个元素,所以将其记录,然后两个序列同时抛弃最后一个元素。假如两个序列的最后元素不相等,那么就再分两种情况,一种是第一个序列抛弃最后一个元素然后在与第二个序列进行比较,另一种是第二个序列抛弃最后一个元素然后再与第一个元素进行比较,之后的过程其实就是一个重复之前操作的过程。

程序如下:

#include<iostream>
using namespace std;
int c[100][100];
void LCS(string x, string y) {
  int m = x.size(), n = y.size();
  for (int i = 1; i <= m; i++) c[i][0] = 0;
  for (int j = 1; j <= n; j++) c[0][j] = 0;
  for (int i = 1; i <= m; i++)
    for (int j = 1; j <= n; j++)
      if (x[i-1] == y[j-1])
        c[i][j] = c[i-1][j-1] + 1;
      else
        c[i][j] = max(c[i-1][j], c[i][j-1]);
}
void printLCS(int i, int j, string x) {
  if (i == 0 || j == 0) return;
  if (c[i][j] == c[i-1][j]) {
    printLCS(i-1, j, x);
  } else if (c[i][j] == c[i][j-1]) {
    printLCS(i, j-1, x);
  } else {
    printLCS(i-1, j-1, x);
    cout << x[i-1];
  }
}
int main() {
  string s1, s2;
  cin >> s1 >> s2;
  LCS(s1, s2);
  printLCS(s1.size(), s2.size(), s1);
}

矩阵连乘问题

设 p[i] 为第i个矩阵的列,p[0]为第1个矩阵的行,m[i][j]表示从第i个矩阵到第j个矩阵连乘的最小乘法次数。

 m[i][j]= { 0                                                         i=j
          { min(i≤k<j){ m[i][k] + m[k+1][j]} + p[i-1]*p[k]*p[j]      i<j

再用s[i][j]记录矩阵的分割位置,即s[i][j] = k

程序为

#include<iostream> 
using namespace std; 
const int N = 10;
int m[N+1][N+1], s[N+1][N+1];
int Compute(int p[], int n) {
  for (int i = 1; i <= n; i++) m[i][i] = 0;
  for (int r = 2; r <= n; r++)
    for (int i = 1; i <= n - r + 1; i++) {
      int j = i + r - 1;
      m[i][j] = m[i+1][j] + p[i-1] * p[i] * p[j];
      s[i][j] = i;
      for (int k = i + 1; k < j; k++) {
        int t = m[i][k] + m[k+1][j] +  p[i-1] * p[k] * p[j];;
        if (t < m[i][j]) {
          m[i][j] = t;
          s[i][j] = k;
        }
      }
    }
    return m[1][n];
}
//打印出具体加括号的情况
void Traceback(int i, int j) {
  if (i == j)
    cout << "A" << i;
  else {
    cout << "(";
    Traceback(i, s[i][j]);
    Traceback(s[i][j] + 1, j);
    cout << ")";
  }
}
int main() {
  int n = 6, p[] = {30,35,15,5,10,20,25};
  cout << Compute(p, n) << endl;
  Traceback(1, 6);
}

0-1背包问题

0-1背包问题:给定n种物品和一个背包,物品i的重量为wi,其价值为vi,背包的承重量为c。问如何选择物品装入背包,使得物品的总价值最大。
物品的重量为:w={ w1,w2,…,wi,…,wn},价值为:v={ v1,v2,…,vi,…,vn},背包容量为c。

设f(i,j)表示在前i个物品中选取物品装入剩余容量为j的背包中,则有

   f(i, 0) = f(0, j) = 0;
   f(i,j)={ max ⁡{ f(i-1,j), f(i-1,j-w(i))+v(i) }        j≥wi
          { f(i-1,j)                                    0≤j<wi

(1)递归解法为

int f(int i, int j) {
  if(i == 0) return 0;
  if(j < w[i]) return f(i-1, j);
  return max(f(i-1,j), f(i-1,j-w[i]) + v[i]);
}

(2)动态规划解法为

#include<iostream> 
using namespace std; 
float m[100][100];

void Knapsack(float v[], int w[], int c, int n) {
  for (int i = 0; i <= n; i++) m[i][0] = 0;
  for (int j = 0; j <= c; j++) m[0][j] = 0;
  for (int i = 1; i <= n; i++) {
    for (int j = 0; j <= c; j++)
      if (j < w[i])
        m[i][j] = m[i-1][j];
      else  
        m[i][j] = max(m[i-1][j], m[i-1][j-w[i]] + v[i]);
  }
}

void traceback(int w[], int c, int n, int x[]) {
  for (int i = n; i > 0; i--)
    if (m[i][c] == m[i-1][c]) {
      x[i] = 0;
    } else {
      x[i] = 1;
      c -= w[i];
    }
}

int main() {
  float v[] = {0,6,3,5,4,6};
  int c = 10, n = 5, w[] = {0,2,2,6,5,4}, x[6];
  Knapsack(v, w, c, n);
  traceback(w, c, n, x);
  cout << "the max value is: " << m[n][c] << endl;
  for(int i = 1; i <= n; i++)
    cout << x[i] <<" ";
}

(3)如果不需要求选取了哪几个物品,则不用二维数组记录每次选物品的情况,只用一个一维数组记录最新的情况,这样程序就简化为:

#include<iostream> 
using namespace std; 
float m[100];
void Knapsack(float v[], int w[], int c, int n) {
  for (int i = 1; i <= n; i++) {
    for (int j = c; j >= w[i]; j--)
      if (m[j-w[i]] + v[i] > m[j])
        m[j] = m[j-w[i]] + v[i];
  }
}
int main() {
  float v[] = {0,6,3,5,4,6};
  int c = 10, n = 5, w[] = {0,2,2,6,5,4};
  Knapsack(v, w, c, n);
  cout << "the max value is: " << m[c] << endl;
}

或:

#include<iostream> 
using namespace std; 
float m[100];
int main() {
  float v[] = {0,6,3,5,4,6};
  int c = 10, n = 5, w[] = {0,2,2,6,5,4};
  for (int i = 1; i <= n; i++)
    for (int j = c; j >= w[i]; j--)
      if (m[j-w[i]] + v[i] > m[j])
        m[j] = m[j-w[i]] + v[i];
  cout << "the max value is: " << m[c] << endl;
}
发布了56 篇原创文章 · 获赞 70 · 访问量 8920

猜你喜欢

转载自blog.csdn.net/weixin_44835732/article/details/103644059
今日推荐