直通BAT--数据结构与算法九(动态规划)

动态规划:
     利用申请的空间来记录每一个暴力搜索的计算结果,下次要用结果的时候直接使用,而不再进行重复的递归过程。且规定每一种递归状态的计算顺序,依次进行计算。

动态规划方法步骤:
  • 暴力搜索方法:递归。
  • 记忆搜索方法:找到哪些参数代表递归过程,将重复计算的值存储下来,是某种形态的动态规划方法。不关心到达某一个递归过程的路径,单纯的对计算过的递归过程进行记录,避免重复的递归过程。
  • 动态规划方法:规定好每一个递归过程的计算顺序,依次进行计算,后面的计算过程严格依赖前面的计算过程。
  • 状态继续化简后的动态规划方法:根据路径化简动态规划方法。

1.换钱的方法数
     给定数组arr,arr中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim。代表要找的钱数,求换钱有多少种方法。

//暴力递归方法

int ExchangeCoins::exchangeCoins(vector<int> arr, int aim){
     if (arr.empty() || aim < 0){
          return 0;
     }
     int res = recProcess(arr, 0, aim);

     return res;
}

int recProcess(vector<int> arr, int index, int aim){
     int res = 0;
     if (index == arr.size()-1){
          res = aim%arr[index] == 0 ? 1 : 0;
     }
     else{
          for (int i = 0; i * arr[index] <= aim; ++i){
               res += recProcess(arr, index + 1, aim - arr[index] * i);
          }
     }
     return res;
}


//记忆搜索方法

int ExchangeCoins::exchangeCoins(vector<int> arr, int aim){
     if (arr.empty() || aim < 0){
          return 0;
     }

     int** table = new int*[arr.size()]; //开辟行
     for (int i = 0; i < arr.size(); i++)
          table[i] = new int[aim + 1]; //开辟列
     for (int i = 0; i < arr.size(); i++)//赋初值
     for (int j = 0; j < aim + 1; j++){
          table[i][j] = 0;
     }
     int res = recProcess(arr, 0, aim, table);

     return res;
}

int recProcess(vector<int> arr, int index, int aim, int** table){
     int res = 0;
     if (index == arr.size()-1){
          res = aim%arr[index] == 0 ? 1 : 0;
     }
     else{
          for (int i = 0; i * arr[index] <= aim; ++i){
              int tableValue = table[index + 1][aim - i * arr[index]];
              if (tableValue == 0){
                   res += recProcess(arr, index + 1, aim - arr[index] * i,table);
              }
              else{
                   res += tableValue == -1 ? 0 : tableValue;
              }
          }
     }
     table[index][aim] = res == 0 ? -1 : res;
     return res;
}

//动态规划方法

int ExchangeCoins::exchangeCoins(vector<int> arr, int aim){

     if (arr.empty() || aim < 0){
          return 0;
     }
     vector<vector<int> > dp(arr.size(), vector<int>(aim + 1));
     for (int i = 0; i < arr.size(); ++i){
          dp[i][0] = 1;
     }
     for (int j = 0; j * arr[0] < aim + 1; ++j){
          dp[0][j * arr[0]] = 1;
     }
     for (int i = 1; i < arr.size(); ++i){
          for (int j = 1; j < aim + 1; ++j){
              for (int k = 0; k*arr[i] <= j; ++k){
                   dp[i][j] += dp[i - 1][j-k*arr[i]];
              }
          }
     }

     int res = dp[arr.size() - 1][aim];
     return res;
}

//动态规划化简

int ExchangeCoins::exchangeCoins(vector<int> arr, int aim){
     if (penny.empty() || aim < 0){
          return 0;
     }

     vector<int> dp(aim + 1);

     for (int j = 0; j*penny[0] <= aim; ++j){
          dp[j*penny[0]] = 1;
     }

     for (int i = 1; i < penny.size(); ++i){
          for (int j = 1; j < aim + 1; ++j){
              dp[j] += j >= penny[i] ? dp[j - penny[i]] : 0;
          }
     }

     return dp[aim];
}



2.台阶问题

//有n级台阶,一个人每次上一级或者两级,问有多少种走完n级台阶的方法。为了防止溢出,请将结果Mod 1000000007
//
//给定一个正整数int n,请返回一个数,代表上楼的方式数。保证n小于等于100000。
//
//测试样例:
//1
//返回:1

int GoUpstairs::countWays(int n){

     //当0<=n<=2时,直接返回
     if (n == 0 || n == 1 || n == 2){
          return n;
     }

     int res = 0;
     long *dp = new long[n + 1];
     dp[1] = 1;
     dp[2] = 2;
     //当前的方法数等于前一阶再踏一步,前两阶再踏两步的方法数之和。
     for (int i = 3; i <= n; ++i){
          dp[i] = (dp[i - 1] + dp[i - 2]) % 1000000007;
     }

     return dp[n];
}


3.矩阵最小路径

//有一个矩阵map,它每个格子有一个权值。从左上角的格子开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,返回所有的路径中最小的路径和。
//
//给定一个矩阵map及它的行数n和列数m,请返回最小路径和。保证行列数均小于等于100.
//
//测试样例:
//[[1, 2, 3], [1, 1, 1]], 2, 3
//返回:4

int MatrixMinPath::matrixMinPath(vector<vector<int> > map, int n, int m){
     if (n <= 0 && m <= 0){
          return 0;
     }
     vector<vector<int> > dp(n, vector<int>(m,0));
     dp[0][0] = map[0][0];
     //第一行的每个值为前面的累加
     for (int i = 1; i < m; ++i){
          dp[0][i] = dp[0][i - 1] + map[0][i];
     }
     //第一列的每个值为前面的累加
     for (int j = 1; j < n; ++j){
          dp[j][0] = dp[j-1][0] + map[j][0];
     }
     //第i,j个的值为同行前一列,同列前一行的最小值加上当前的map值
     for (int i = 1; i < n; ++i){
          for (int j = 1; j < m; ++j){
              dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + map[i][j];
          }
     }
     return dp[n-1][m-1];
}


4.最长上升子序列

//这是一个经典的LIS(即最长上升子序列)问题,请设计一个尽量优的解法求出序列的最长上升子序列的长度。
//
//给定一个序列A及它的长度n(长度小于等于500),请返回LIS的长度。
//
//测试样例:
//[1, 4, 2, 5, 3], 5
//返回:3

int LongestIncSubsequence::getLIS(vector<int> A, int n){
     if (n <= 0){
          return 0;
     }
     if (n == 1){
          return 1;
     }
     vector<int> dp(n,1);
     dp[0] = 1;
     int res = 0;
     //遍历数组
     for(int i = 1; i < n; ++i){
          //找到以该位置结束(含有该值),最长上升子序列的长度,即计算小于该值的个数+1的值
          for (int j = i-1; j >= 0; --j){
              if (A[j] < A[i]){
                   dp[i] = max(dp[i], dp[j] + 1);
              }
          }
          //res记录在该位置及以前最长上升子序列长度
          res = max(res, dp[i]);
     }
     return res;
}



5.最长公共子序列的长度

//给定两个字符串A和B,返回两个字符串的最长公共子序列的长度。例如,A = "1A2C3D4B56”,B="B1D23CA45B6A”,”123456"或者"12C4B6"都是最长公共子序列。
//
//给定两个字符串A和B,同时给定两个串的长度n和m,请返回最长公共子序列的长度。保证两串长度均小于等于300。
//
//测试样例:
//"1A2C3D4B56", 10, "B1D23CA45B6A", 12
//返回:6

int LongestCommonSubstring::findLCS(string A, int n, string B, int m){
     if (n <= 0 || m <= 0){
          return 0;
     }
     vector<vector<int> > dp(n + 1, vector<int>(m + 1, 0));
     for (int i = 1; i <= n; ++i){
          for (int j = 1; j <= m; ++j){
              if (A[i-1] == B[j-1]){
                   dp[i][j] = max(max(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1] + 1);
              }
              else{
                   dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
              }
          }
     }
     return dp[n][m];
}



6.01背包问题

//一个背包有一定的承重cap,有N件物品,每件都有自己的价值,记录在数组v中,也都有自己的重量,记录在数组w中,每件物品只能选择要装入背包还是不装入背包,要求在不超过背包承重的前提下,选出物品的总价值最大。
//
//给定物品的重量w价值v及物品数n和承重cap。请返回最大总价值。
//
//测试样例:
//[1, 2, 3], [1, 2, 3], 3, 6
//返回:6

int Backpack::maxValue(vector<int> w, vector<int> v, int n, int cap){
     if (n <= 0){
          return 0;
     }
     //构建一个二维表,行数表示w数组,列数表示已有的重量,数组中的值表示价值
     vector<vector<int> > dp(n + 1, vector<int>(cap + 1,0));

     for (int i = 1; i <= n; ++i){
          int m = i - 1;
          for (int j = 1; j <= cap; ++j){
              //如果j >= w[m],第i,j所在的点价值dp[i][j],为不放入w[i]的价值(dp[i-1][j])与放入w[i](dp[i-1][j-w[i-1]]+v[i-1])中较大的那一个
              if (j >= w[m]){
                   dp[i][j] = max(dp[i - 1][j], dp[i-1][j - w[m]] + v[m]);
              }
              //如果j < w[m],则第i,j所在的点价值dp[i][j]等于上一个dp[i-1][j]
              else{
                   dp[i][j] = dp[i-1][j];
              }
          }
     }
     return dp[n][cap];
}


7.字符串最优编辑

//对于两个字符串A和B,我们需要进行插入、删除和修改操作将A串变为B串,定义c0,c1,c2分别为三种操作的代价.
//请设计一个高效算法,求出将A串变为B串所需要的最少代价。
//c0 插入
//c1 删除
//c2 修改
//
//给定两个字符串A和B,及它们的长度和三种操作代价,请返回将A串变为B串所需要的最小代价。
//保证两串长度均小于等于300,且三种代价值均小于等于100。
//
//测试样例:
//"abc", 3, "adc", 3, 5, 3, 100
//返回:8

int StringTransferMinCost::findMinCost(string A, int n, string B, int m, int c0, int c1, int c2){
     if (n < 0 || m < 0){
          return 0;
     }

     vector<vector<int> > dp(n + 1, vector<int>(m + 1, 0));
     //第0列,即空字符形成B字符串,需要一个个插入
     for (int i = 0; i <= n; ++i){
          dp[i][0] = i*c1;
     }
     //第0行,即A字符串形成空字符,需要一个个删除
     for (int j = 0; j <= m; ++j){
          dp[0][j] = j*c0;
     }
     for (int i = 1; i <= n; ++i){
          for (int j = 1; j <= m; ++j){
              //如果A[i-1] == B[j-1],则dp[i][j]不改变直接取dp[i - 1][j - 1];
              if (A[i-1] == B[j-1]){
                   dp[i][j] = dp[i - 1][j - 1];
              }
              //如果A[i-1] != B[j-1],则dp[i][j]取dc,rc,ic中小的一个
              //dp[i][j]表示从A[0]到A[i]已经转换为B[0]到B[j]用的代价,此时A = B;
              //dc为dp[i - 1][j]基础上删除一个,即从dp[i-1][j]到dp[i][j],表示A增加一个字符,B没有增加,把A增加的那个删掉
              //rc为dp[i - 1][j - 1]基础上修改一个,即从dp[i-1][j-1]到dp[i][j],表示A增加一个字符,B增加一个字符,把两个增加的修改
              //ic为dp[i][j - 1]基础上插入一个,即从dp[i][j-1]到dp[i][j],表示A未增加,B增加一个字符,把A增加一个字符
              else{
                   int ic = INT_MAX, dc = INT_MAX, rc = INT_MAX;
                   dc = dp[i - 1][j] + c1;
                   rc = dp[i - 1][j - 1] + c2;
                   ic = dp[i][j - 1] + c0;
                   dp[i][j] = min(ic, min(dc, rc));
              }
          }
     }
     return dp[n][m];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值