一、一般步骤
- 首先写出题目的暴力递归版本
- 观察在递归版本中变量是哪几个,然后根据变量建立动态规化的数组,明确动态规化方程的含义是什么
- 根据动态规化数组填充的方式决定计算的顺序以及动态规化初始条件和动态规化方程。并且要明确我们要求的是动态规化方程的哪一项
二、例题
1、字符串问题
题目:规定1和A对应、2和B对应、3和C对应……
那么一个数字字符串比如“111”,就可以转化为“AAA”、“KA”、“AK”。给定一个只有数字字符组成的字符串str,返回有多少种转化的结果。
求解:
首先,得出暴力递归的方法:
//考虑i位置的字符
//函数返回的是从i位置的字符到最后得到的转化方法数
int process(string s,int i)
{
//i到最后,说明没有字符了,但是前面的转化是成功的
//也就是返回前面确定的方法也就是一种
if(i==s.size())
return 1;
//因为没有字符是0
//说明前面转化的方法也是没有用的
//方法数就是0
if(s[i]=='0')
return 0;
if(s[i]=='1')
{
int res = process(s,i+1);
if(i+1<s.size())
{
res+=process(s,i+2);
}
return res;
}
else if(s[i]=='2')
{
int res = process(s,i+1);
if(i+1<s.size()&&s[i+1]>='0'&&s[i+1]<='6')
res+=process(s,i+2);
return res;
}
else
return process(s,i+1);
}
int main()
{
cout<<process("111",0)<<endl;
return 0;
}
第二步,观察暴力递归函数,可以看出变化的参数只有i,因此动态规化数组只需要一个一维数组,d[i]表示的是从字符串的i位置到最后可以得到的所有方法数。
第三步,观察动态规化数组填满的方向。观察暴力递归可以看出我们是根据i+1计算i,所有动态规化的方向是从后到前。动态规化的初始条件是d[s.size()]=1,所求的结果是d[0]。之后根据暴力递归的过程写出动态规化的内容即可。
代码:
//动态规化的初始条件是dp[s.size()]=1
//动态规化的方向是后往前
//dp[0]是最后的结果
//dp[i]表示的是从字符串i位置到字符串的末尾所有的方法数的总和
int main()
{
string s;
cin>>s;
int dp[100];
memset(dp,0,sizeof(dp));
int len = s.size();
//动态规化初始条件
//当达到字符串长度的时候返回的方法数是1
dp[len] = 1;
//动态规化方程
for(int i = len-1;i>=0;--i)
{
//说明这种转化方式是不可行的
if(s[i]=='0')
dp[i] = 0;
else if(s[i]=='1')
{
dp[i] = dp[i+1];
if(i+1<len)
dp[i]+=dp[i+2];
}
else if(s[i]=='2')
{
dp[i] = dp[i+1];
if(i+1<len&&s[i+1]>='0'&&s[i+1]<='6')
dp[i]+=dp[i+2];
}
else
dp[i] = dp[i+1];
}
cout<<dp[0]<<endl;
return 0;
}
2、背包问题
题目:给定两个长度为N灯的数组weight和values,weights[i]和values[i]分别代表i号物品的重量和价值。给定一个整数bag,表示一个载重bag的袋子,你装的物品不能超过这个重量。返回你能装下的最多的价值是多少?
首先,仍然是暴力递归的内容:
//返回的是当剩余空间为room的时候考虑index到weights.size()物品的时候最大的价值
//总体来说,需要考虑每个物品是选择还是不选择
int process(vector<int> weights,vector<int> values,int room,int index)
{
if(room<=0)
return 0;
if(index==weights.size())
return 0;
int res1 = process(weight,values,room,index+1);
if(room>=weights[index])
int res2 = process(weights,values,room-weights[index],index+1)+values[index];
return max(res1,res2);
}
然后,从暴力递归的过程中可以看出变化的是物品的序号和剩余的空间,那么动态规化的方程就是一个二维数组。dp[i][j]表示的是当剩余空间为j的时候考虑到物品i最大的价值。
最后,初始条件为dp[weight.size()][……] = 0;从暴力递归的过程中可以看出动态规化的方向是index从大到小,room从小到大;d[0][0]就是我们问题的解。
代码:
int main()
{
int n;
vector<int> weights;
vector<int> values;
int bag;
cin>>n;
for(int i = 0;i<n;++i)
{
cin>>weights[i]>>values[i];
}
cin>>bag;
int d[100][100];
memset(d,0,sizeof(d));
//d[n][……]为0
//动规化
for(int i = n-1;i>=0;--i)
{
for(int j = 1;j<=bag;++j)
{
d[i][j] = d[i+1][j];
if(j>=weights[i])
{
d[i][j] = max(d[i][j],d[i+1][j-weights[i]]+values[i]);
}
}
}
cout<<d[0][0]<<endl;
return 0;
}
人间理想-----肖战