这周着重学习动态规划,看了看博客并自己刷了一些题目,总结一下动态规划的做题思路。
首先设计状态,然后写出状态转移方程,设定初始状态,之后进行状态转移并求出最终的解。
其中最让我头疼的还是设计状态和写出状态转移方程,有好多题目状态不明显,根本就想不到。
1.P1255 数楼梯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这是一道最基础简单的动态规划问题,是我第一道写的,状态找不到,看了题解才搞清楚
由于到第i级台阶的方法只有两种,从第i-1级到的和第i-2级到的,所以
dp[i]=dp[i-1]+dp[i-2] dp[i]是到第i级所能走的方法数
#include<iostream>
using namespace std;
int main()
{
long long int dp[5005],n;
dp[0]= {1};
dp[1]= {1};
cin>>n;
for(int i=2; i<=n; i++)
dp[i]=dp[i-1]+dp[i-2];
cout<<dp[n];
}
但是最后longlong也不够用,结果还是太大,高精度不会写就放弃了。
2.P1002 [NOIP2002 普及组] 过河卒 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
有了上一次的经验,这次写状态转移方程就轻松了许多,到达(i,j)点的走法只有从(i-1,j)和(i,j-1)两种,因此dp(i,j)=dp(i-1,j)+dp(i,j-1),这道题需要注意马的位置,经过马所控制的区域时只需要将此时的dp赋为0即可
#include<iostream>
using namespace std;
int main()
{
long long int dp[30][30];
int n,m,a,b;
cin>>n>>m>>a>>b;
if(b!=0)
for(int i=0; i<=n; i++)
dp[i][0]=1;
else
{
for(int i=0; i<a; i++)
dp[i][0]=1;
for(int i=a; i<=n; i++)
dp[i][0]=0;
}
if(a!=0)
for(int i=0; i<=m; i++)
dp[0][i]=1;
else
{
for(int i=0; i<b; i++)
dp[0][i]=1;
for(int i=b; i<=m; i++)
dp[0][i]=0;
}
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
if((i==a-1&&j==b-2)||(i==a-1&&j==b+2)||(i==a+1&&j==b-2)||(i==a+1&&j==b+2)||(i==a-2&&j==b-1)||(i==a-2&&j==b+1)||(i==a+2&&j==b-1)||(i==a+2&&j==b+1)||(i==a&&j==b))
dp[i][j]=0;
else
dp[i][j]=dp[i-1][j]+dp[i][j-1];
cout<<dp[n][m];
}
分支判断很多主要还是因为不会用更简单的方法来给出条件
这道题看着很复杂,实际上有了之前做题的经验,状态转移方程还是很好写的,在偷到第x个房间之前,能偷的最大钱数为前一个房间的钱数加这个房间的钱数或者上一个房间的钱数这两种情况,所以dp[i]=max(dp[i-2]+num[i],dp[i-1])有了状态转移方程这道题目就很好做了
#include<iostream>
using namespace std;
int max(int a,int b)
{
return a>b?a:b;
}
int rob(int *nums,int n)
{
int i;
int dp[110];
dp[0]=nums[0];
for(i=0; i<n; ++i)
{
if(i==1)
dp[1]=max(nums[0],nums[1]);
else
{
dp[i]=max(dp[i-2]+num[i],dp[i-1]);
}
}
}
总的来说,动态规划在清楚知晓状态转移方程之后难度不是很大,但他难就难在对题目状态的提取
其中有些题目实际可以用贪心来做,但是一遇到有负数等的题目时,贪心这种目光短浅的算法就会失效,只能用动态规划来解决
动态规划学无止境,上面的三道题还只是普普通通的题目,真遇上难题我也是做不出来的,但是动态规划这种思想简洁方便,能很好解决超时的问题,很值得去多学习思考
算法课程也即将结束,这半年收获也是不少,感觉我对算法可能还挺感兴趣的?