杭电oj(1087、1203、1003)题解

DP 即动态规划(Dynamic Programming),是一种通过把原问题分解为相对简单的子问题,并保存子问题的解来避免重复计算,从而解决复杂问题的算法策略。以下从几个方面简述动态规划:

基本思想

动态规划的核心在于 "分治" 和 "最优子结构"。它将一个复杂问题分解为一系列相互关联的子问题,通过求解子问题并保存其解,避免对相同子问题的重复求解,从而减少计算量。同时,原问题的最优解可以由子问题的最优解组合而成,这就是最优子结构性质。

适用条件

  • 最优子结构性质 :问题的最优解包含其子问题的最优解。即可以通过子问题的最优解推导出原问题的最优解。例如,在背包问题中,对于一个容量为 W 的背包和 n 个物品,要得到其最优装法,可以先考虑容量为 W - w[i]w[i] 为第 i 个物品的重量)的背包和前 n - 1 个物品的最优装法。
  • 子问题重叠性质:在求解过程中,会多次遇到相同的子问题。动态规划通过保存子问题的解,避免了对这些子问题的重复计算,从而提高了效率。比如在斐波那契数列的计算中,直接递归计算会有大量重复的子问题计算,而使用动态规划可以避免这种情况。

求解步骤

  1. 定义状态 :明确问题的状态表示,即如何用一个或多个变量来描述子问题。例如,在最长上升子序列问题中,可以定义状态 dp[i] 表示以第 i 个元素结尾的最长上升子序列的长度。
  2. 确定状态转移方程 :根据问题的最优子结构性质,找出状态之间的转移关系。状态转移方程描述了如何从子问题的解推导出原问题的解。例如,在最长上升子序列问题中,状态转移方程为 dp[i] = max(dp[j] + 1) (0 <= j < i 且 a[j] < a[i])
  3. 初始化 :确定初始状态的值,这些初始状态是问题的边界条件。例如,在斐波那契数列的动态规划求解中,dp[0] = 0dp[1] = 1 就是初始状态。
  4. 计算顺序:根据状态转移方程,确定计算状态的顺序,一般是从简单的子问题开始,逐步求解更复杂的问题。

应用场景

动态规划在很多领域都有广泛的应用,常见的问题包括:

  • 背包问题:如 0 - 1 背包问题、完全背包问题等,用于在一定的约束条件下选择物品,使得总价值最大。
  • 最长公共子序列问题:找出两个序列中最长的公共子序列,在生物信息学、版本控制等领域有应用。
  • 最短路径问题:如 Floyd - Warshall 算法,用于求解图中任意两点之间的最短路径。
  • 资源分配问题:在多个项目或任务之间分配资源,以达到最优的效益。

目录

1087

题目

思路

代码详细步骤

动态规划初始化

状态转移

找出最大值

代码

1203

题目

思路

代码

1003

题目

思路

代码


1087

题目

思路

动态规划算法的核心在于将原问题分解为一系列子问题,并通过求解子问题来得到原问题的解。在这个问题中,我们定义状态 dp[i] 表示以第 i 个元素结尾的最长上升子序列的最大和。通过遍历序列中的每个元素,我们可以根据之前的状态来更新当前状态,最终找出所有状态中的最大值,即为最长上升子序列的最大和。

代码详细步骤

动态规划初始化
  • dp[0]=a[0];:将 dp[0] 初始化为 a[0],因为以第一个元素结尾的最长上升子序列就是它本身,其和就是 a[0]
状态转移
  • 外层循环 for(int i = 1; i < n; i++):遍历数组中的每个元素,从第二个元素开始。
  • dp[i]=a[i];:将 dp[i] 初始化为 a[i],表示以第 i 个元素结尾的最长上升子序列至少包含该元素本身。
  • 内层循环 for(int j = 0; j < i; j++):遍历当前元素 a[i] 之前的所有元素。
  • if(a[i]>a[j]):判断 a[i] 是否大于 a[j],如果满足条件,说明可以将 a[i] 接到以 a[j] 结尾的最长上升子序列后面。
  • dp[i]=max(a[i]+dp[j],dp[i]);:更新 dp[i] 的值,取 a[i] + dp[j]dp[i] 中的最大值。a[i] + dp[j] 表示将 a[i] 接到以 a[j] 结尾的最长上升子序列后面得到的新子序列的和。
找出最大值
  • int maxnum=-1e9;:将 maxnum 初始化为一个较小的值 -1e9,确保后续找到的任何 dp[i] 值都能更新 maxnum
  • for(int i = 0; i < n; i++){ maxnum=max(maxnum,dp[i]); }:遍历 dp 数组,找出其中的最大值。

代码

cpp 复制代码
#include<iostream>
#include<algorithm>
using namespace std;
int dp[1010],a[1010],n; 
int main(){
	while(cin>>n&&n){
		for(int i=0;i<n;i++){
			cin>>a[i];
		}
		dp[0]=a[0];
		for(int i=1;i<n;i++){
			dp[i]=a[i];
			for(int j=0;j<i;j++){
				if(a[i]>a[j]){
					dp[i]=max(a[i]+dp[j],dp[i]);
				}
			}
		}
		int maxnum=-1e9;
		for(int i=0;i<n;i++){
			maxnum=max(maxnum,dp[i]);
		}
		cout<<maxnum<<endl;
	}
	return 0;
}

1203

题目

思路

外层循环 for(int i = 0; i < m; i++):遍历每一个物品。

内层循环 for(int j = n; j >= a[i]; j--):从背包的总容量 n 开始,倒序遍历到当前物品的重量 a[i]。倒序遍历是为了保证每个物品只被选择一次,这是 0 - 1 背包问题的常见处理方式。

状态转移方程 dp[j]=max(dp[j],dp[j-a[i]]+prob[i]-dp[j-a[i]]*prob[i]);

dp[j] 表示不选择当前物品 i 时,背包容量为 j 的最大成功概率。

dp[j - a[i]] 表示在不放入当前物品 i 时,背包容量为 j - a[i] 的最大成功概率。

dp[j - a[i]] + prob[i] - dp[j - a[i]] * prob[i] 表示选择当前物品 i 时的成功概率。这里基于概率的计算原理,假设物品的成功是相互独立事件,使用公式 P(A 或 B) = P(A) + P(B) - P(A) * P(B) 来计算选择当前物品后的成功概率。

通过 max 函数取两者中的最大值更新 dp[j]

代码

cpp 复制代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
double dp[N];
double prob[N];
int a[N];
int n,m;
int main(){
	while(cin>>n>>m){
		if(n==0 && m==0) break;
		for(int i=0;i<m;i++){
			cin>>a[i]>>prob[i];
		}
		memset(dp,0,sizeof(dp));
		for(int i=0;i<m;i++){
			for(int j=n;j>=a[i];j--){
				dp[j]=max(dp[j],dp[j-a[i]]+prob[i]-dp[j-a[i]]*prob[i]);
			}
		}
		printf("%.1lf%%\n",dp[n]*100);
	}
	return 0;
}

1003

题目

思路

状态转移方程 dp[j]=max(dp[j],dp[j-a[i]]+prob[i]-dp[j-a[i]]*prob[i]);

dp[j] 表示不选择当前物品 i 时,背包容量为 j 的最大成功概率。

dp[j - a[i]] 表示在不放入当前物品 i 时,背包容量为 j - a[i] 的最大成功概率。

dp[j - a[i]] + prob[i] - dp[j - a[i]] * prob[i] 表示选择当前物品 i 时的成功概率。这里基于概率的计算原理,假设物品的成功是相互独立事件,使用公式 P(A 或 B) = P(A) + P(B) - P(A) * P(B) 来计算选择当前物品后的成功概率。

通过 max 函数取两者中的最大值更新 dp[j]

代码

cpp 复制代码
#include<iostream>
using namespace std;
int n,m,l,r,maxnum,sum,temp,num;
int main(){
	cin>>n;
	for(int i=0;i<n;i++){
		l=0,r=0,temp=1,maxnum=-20000,sum=0;
		cin>>m;
		for(int j=1;j<=m;j++){
			cin>>num;
			sum+=num;
			if(sum>maxnum){
				r=j;
				maxnum=sum;
				l=temp;
			}
			if(sum<0){
				temp=j+1;
				sum=0;
			}
		}
		cout<<"Case "<<(i+1)<<":"<<endl<<maxnum<<" "<<l<<" "<<r<<endl;
		if(i<n-1) cout<<endl;
	}
	return 0;
}
相关推荐
写个博客16 分钟前
代码随想录算法训练营第二十六天
算法
天才测试猿28 分钟前
软件测试之功能测试详解
自动化测试·软件测试·python·功能测试·测试工具·职场和发展·测试用例
阳光男孩0135 分钟前
第十四届蓝桥杯省B.砍树
职场和发展·蓝桥杯
海底火旺1 小时前
寻找缺失的最小正整数:从暴力到最优的算法演进
javascript·算法·面试
优美的赫蒂1 小时前
理解欧拉公式
线性代数·算法·数学建模
Pasregret1 小时前
策略模式:动态切换算法的设计智慧
算法·bash·策略模式
努力学习的小廉1 小时前
【C++】 —— 笔试刷题day_21
开发语言·c++·算法
周杰伦_Jay2 小时前
continue插件实现IDEA接入本地离线部署的deepseek等大模型
java·数据结构·ide·人工智能·算法·数据挖掘·intellij-idea
江沉晚呤时2 小时前
深入了解递归、堆与栈:C#中的内存管理与函数调用
java·jvm·算法
愚润求学2 小时前
【专题刷题】二分查找(一):深度解刨二分思想和二分模板
开发语言·c++·笔记·leetcode·刷题