problem solving steps
step1:
确定状态:
- 最后一步
- 子问题
step2:
转移方程
step3:
初始条件和边界情况
step4:
确定计算顺序
example1
https://www.lintcode.com/problem/coin-change/
step1:(确定状态)
不妨我们先设总金额共27元,面值为2,5,7
最后一步:
- 虽然我们不知道最优策略是什么,但是最优策略一定是a1,a2,···,ak共k枚硬币加起来面值等于27,所以一定有一枚最后的硬币:ak,那除掉这最后的一枚硬币,前面的硬币面值加起来应该为27-ak
子问题:
- 所以我们就要求:用最少的硬币可以拼出27-ak
- 原问题是最少用多少枚硬币可以拼出27
- 我们将原问题转化为一个子问题,而且规模更小:27-ak
- 为了简化定义,我们设状态f(X)=最少用多少枚硬币拼出X
- 等等,我们还不清楚最后那枚硬币ak是多少
- 最后那枚硬币只能是2,5或者7
- 如果ak为2,f(27)应该是f(27-2)+1(加上最后这枚硬币2)
- 如果ak为5,f(27)应该是f(27-5)+1(加上最后这枚硬币5)
- 如果ak为7,f(27)应该是f(27-7)+1(加上最后这枚硬币7)
- 除此之外,没有其他可能了
- 由于要求最少的硬币数目,故:
- f(27) = min{f(27-2)+1 , f(27-5)+1,f(27-7)+1}
step2(转移方程):
- 设状态f[x]=最少用多少枚硬币拼出X
- 对于任意的X,
step3(初始条件和边界情况):
- f[x] = min{f[x-2]+1 , f[x-5]+1,f[x-7]+1}
- 两个问题:x-2,x-5,x-7小于0怎么办?什么时候停下来?
- 如果不能拼出Y,就定义Y为正无穷:
- 例如f(-1)=f(-2)=···=正无穷
- 初始条件:f[0]=0
- 对于初始条件f[0],若不手动定义,则用转移方程算出来为正无穷,然而f[0]应为0,则需手动定义
step4(计算顺序):
- f[x] = min{f[x-2]+1 , f[x-5]+1,f[x-7]+1}
- 当我们计算f[x]时,f[x-2] , f[x-5],f[x-7]需已知
- 初始条件:f[0]=0
- 然后计算f[1]、f[2]······、f[27]
class Solution {
public:
/**
* @param coins: a list of integer
* @param amount: a total amount of money amount
* @return: the fewest number of coins that you need to make up
*/
int coinChange(vector<int> &coins, int amount) {
// write your code here
vector<int>f(amount+1);
f[0] = 0;
for(int i=1;i <= amount;++i){
f[i] = 100000;//maxvalue
for(int j=0;j<coins.size();++j){
if(i-coins[j]>=0 && coins[j]!=0){//coins[j]!=0:because the array that the question given include number-zero
f[i] = min(f[i-coins[j]]+1,f[i]);
}
}
}
if(f[amount]==100000){
return -1;
}
return f[amount];
}
};
example2
https://www.lintcode.com/problem/114/
step1:确定状态
最后一步
- 无论机器人怎么走到右下角,总有最后一步:向右或者向下
- 右下角坐标设为(m-1,n-1)
- 那么前一步机器人一定在(m-2,n-1)或者(m-1,n-2)
子问题
- 若机器人有M种方式走到(m-2,n-1),有N种方式走到(m-1,n-2),则有M+N种方式走到(m-1,n-1)
- 设f[i][j]代表机器人有多少种方式走到(i,j)
step2:转移方程
f[i][j] = f[i-1][j] + f[i][j-1]
step3:初始条件和边界情况
初始条件:
- f[0][0] = 1,因为机器人只有一种方式走到左上角
边界情况:
- i=0 或 j=0 时,则f[i][j] = 1
step4:计算顺序
- 先行,后列
- f[0][0],f[0][1]…,f[0][j-1]
- …
- f[i-1][0],f[i-1][1],…,f[i-1,j-1]
class Solution {
public:
/**
* @param m: positive integer (1 <= m <= 100)
* @param n: positive integer (1 <= n <= 100)
* @return: An integer
*/
int uniquePaths(int m, int n) {
// write your code here
vector< vector<int> >f(m,vector<int>(n));//int f[m][n]效率高一些
for(int i = 0;i < m; ++i){
for(int j=0;j<n;++j){
if(i==0 || j==0){
f[i][j] = 1;
}else{
f[i][j] = f[i-1][j] + f[i][j-1];
}
}
}
return f[m-1][n-1];
}
};
example 3
https://www.lintcode.com/problem/jump-game/
step1:确定状态
最后一步
- 设最后一步从石头i跳过来
- 需要满足两个条件:
- 1.能跳到石头i
- 2.最后一步不能超过跳跃的最大距离:n-1-i<=ai
子问题
设f[i]代表能不能跳到石头i
step2:转移方程
step3:初始条件和边界情况
初始条件:
f[0] = true
边界情况:
无
step4:计算顺序
从小到大
class Solution {
public:
/**
* @param A: A list of integers
* @return: A boolean
*/
bool canJump(vector<int> &A) {
// write your code here
vector<bool>f(A.size());
f[0] = true;
for(int i=1;i<A.size();++i){
int j;
for(j=0;j<i;++j){
if(f[j]==true && i-j<=A[j]){
f[i] = true;
break;
}
}
if(j==i){
f[i] = false;
}
}
return f[A.size()-1];
}
};