算法之动态规划(思想)

写在前面的话:

动态规划是一种问题求解方法,它本身并不是一个特定的算法,而是一种思想、一种手段。
状态状态转移方程、最优子结构(全局最优解包含局部最优)

三角形上的动态规划

(d为路径距离,a为单个距离)
状态方程:
d(I,j) = a(I,j) + max(d(I+1,j),d(I+1,j+1))

  • 得出递归代码(重复计算):在这里插入图片描述
int solve(int i,int j){
	return a[i][j] + (i==n?0:max(solve(i,j+1),solve(i+1,j+1)));
}
  • 递推计算(从下往上):
int i,j;
for(j = 1;j<=n;j++){  // 最后一行赋值
	d[n][j] = a[n][j];
}
for(i = n-1;i>=1;i--){
	for(j = 1;j<=i-1;j++){
		d[i][j] = a[i][j] + max(b[i+1][j],b[i+1][j+1]);
	}
}
return d[1][1]; // 递推到最上面,一定是最优解
  • 记忆化搜索(仍然使用递归,不过通过初始化来避免重复)
memset(d,-1,sizeof(d)); // 标记
int solve(int i,int j){
    if(d[i][j]) >= 0 return d[i][j];
    return d[i][j] = a[i][j] + (i==n?0:max(solve(i+1,j),solve(i+1,j+1)); 

在这里插入图片描述

DAG上的动态规划

有向无环图上的动态规划是学习动态规划的基础,很多问题都可以转化为DAG上的最长路、最短路或路径计数问题。

  • 分析矩形可嵌套
    可嵌套关系是一个典型的二元关系,而二元关系可以用图来建模。如果X能被嵌套在Y中,则X,Y中间有一条有向边连接。
    没有确定的终点和起点
  • 分析硬币问题
    设初始状态为S,终点为0
    本质上也是一个路径问题,通过使用硬币,实现状态的转换。
    考虑从状态S开始的最长路径:
    状态转换公式也是:d(i) = max(d(j)+1)(i到j能够转换,必须满足V[i] <= i)
    以下是代码
memset(d,-1,sizeof(d)); // 初始化所有的状态都没有访问过
int dp(int s){
    int& ans = d[s];
    int(ans != -1) return ans;  // 被访问过(得到的可能是路径长,也可能代表无法到达0状态)
    ans = -(1<<30);  // 不成立 和-1 和 其他正数区别开来
    for(int i = 1;i <= n;i++){
    	if(s >= V[i]){  // 满足条件,可以转换
    	    ans = max(ans,dp(s-V[i])+1;
    	}
    }
    return ans;
}

如果既要求最大值也要求最小值,则需要写两个记忆化搜索,比较麻烦。

// 最大值
memset(d,-1,sizeof(d)); // 初始化所有的状态都没有访问过
int dp(int s){
    int& ans = d[s];
    int(ans != -1) return ans;  // 被访问过(得到的可能是路径长,也可能代表无法到达0状态)
    ans = -(1<<30);  // 不成立 和-1 和 其他正数区别开来
    for(int i = 1;i <= n;i++){
     if(s >= V[i]){  // 满足条件,可以转换
         ans = max(ans,dp(s-V[i])+1;
     }
    }
    return ans;
}
// 最小值
memset(d,-1,sizeof(d)); // 初始化所有的状态都没有访问过
int dp(int s){
    int& ans = d[s];
    int(ans != -1) return ans;  // 被访问过(得到的可能是路径长,也可能代表无法到达0状态)
    ans = (1<<30);  // 不成立 和-1 和 其他正数区别开来
    for(int i = 1;i <= n;i++){
     if(s >= V[i]){  // 满足条件,可以转换
         ans = min(ans,dp(s-V[i])+1;
     }
    }
    return ans;
}

此时用递推更加方便:

for(int i = 1;i<=S;i++){
    minv[i] = INF;
    maxv[i] = -INF;
}
for(int i = 1;i<=S;i++){
    for(int j = 1;j<=n;j++){
    	if(i>=V[j){
    	    minv[i] = min(minv[i],minv[i-v[j]]+1);
    	    maxv[i] = max(maxv[i],maxv[i-v[j]]+1);
}
cout << minv[S] << maxv[S] << endl;

发布了45 篇原创文章 · 获赞 0 · 访问量 1005

猜你喜欢

转载自blog.csdn.net/jokerxsy/article/details/104142317