DP(动态规划)全称Dynamic Programming,是运筹学的一个分支,是一种将复杂问题分解成很多重叠的子问题、并通过子问题的解得到整个问题的解的算法。
在动态规划中有一些概念:
n<=1e3 [][] ,n<=100 [][][]
状态:就是形如dp[i][j]= val的取值,其中i,j为下标,也是用于描述、确定状态所需的变量,val为状态值。
状态转移:状态与状态之间的转移关系,一般可以表示为一个数学表达式,转移方向决定了迭代或递归方向。
最终状态:也就是题目所求的状态,最后的答案
1.确定状态,一般为“到第i个为止,xx为j(xx为k)的方案数/最小代价/最大价值”可以根据数据范围和复杂度来推理。
2.确定状态转移方程,即从已知状态得到新状态的方法,并确保按照这个方向一定可以正确地得到最终状态。
根据状态转移的方向来决定使用选代法还是递归法记忆化。
3.确定最终状态并输出。
数字三角形
蓝桥杯数字三角形
思路:可以用 dp也可以用动态规划,计算最大和,再判断向下和向右操作不大于 1。
- 动态规划
O(n^3)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e2 +5;
int n,a[N][N],dp[N][N][N];int main(){memset(dp,-0x3f,sizeof(dp));cin>>n;for(int i=1;i<=n;i++)for(int j=1;j<=i;j++)cin>>a[i][j];dp[1][1][0] = a[1][1];for(int i=2;i<=n;i++)for(int j=1;j<=i;j++){for(int k=0;k<=n-1;k++){if(!k)dp[i][j][k] = dp[i-1][j-1][k] + a[i][j];else dp[i][j][k] = max(dp[i-1][j-1][k],dp[i-1][j][k-1]) + a[i][j];}}int ans=0;if((n-1)&1) for(int j=1;j<=n;j++) ans = max(ans,max(dp[n][j][(n-1)/2+1],dp[n][j][(n-1)/2]));else for(int j=1;j<=n;j++) ans = max(ans,dp[n][j][(n-1)/2]);cout<<ans<<'\n';return 0;
}
思路:由于最后的位置是有规律的,所以直接用[][]就行。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e2 +5;
int n,a[N][N],dp[N][N];int main(){cin>>n;for(int i=1;i<=n;i++)for(int j=1;j<=i;j++)cin>>a[i][j];dp[1][1] = a[1][1];for(int i=2;i<=n;i++)for(int j=1;j<=i;j++)dp[i][j] = max(dp[i-1][j-1],dp[i-1][j]) + a[i][j];if((n-1)&1)cout<<max(dp[n][(n-1)/2+1],dp[n][(n-1)/2+1+1]);else cout<<dp[n][(n-1)/2+1];return 0;
}
思路:用 DFS,代码结果不对,不知道为什么
#include<bits/stdc++.h>
using namespace std;
const int N = 1e2+10;
int a[N][N],res[N][N],n;int dfs(int i,int j){if(res[i][j])return res[i][j];if(i==n){if(n%2==0&&(j==(n-1)/2+1||j==(n-1)/2+1+1))return a[i][j];if(n%2==1&&j==(n-1)/2+1)return a[i][j];return -10000000;}return res[i][j] = max(dfs(i+1,j),dfs(i+1,j+1))+a[i][j];
}int main( ){cin>>n;for(int i=1;i<=n;i++)for(int j=1;j<=i;j++)cin>>res[i][j];cout<<dfs(1,1)<<'\n';return 0;
}