动态规划
• 首先动态规划是什么,由前面的状态推出后面的状态,这个定义看起来就像一的递归.
• 好像动态规划牛逼的地方就在于,它把一些前面的状态保存下来了,不用想暴力那样重复计算多次.这个如果对一些问题画一个递归树就能体会出来.
• 五部曲:跟carl哥学的dp五部曲(有方法论真的很重要)
- 搞清楚dp[i]和i的含义.
- 状态转移方程(递推公式)
- dp数组初始化
- 如何遍历dp数组
- 举几组例子验证
• 今天做的一个例子,后面还会继续总结归纳递归的不同题型,这五天可能都是这个
leetcode:746 使用最小花费爬楼梯
套用一下五部曲:
- 用dp[i]来表示爬到第i级楼梯的最小花费(最优解)
- 如果已经爬到了第i-2级,那么爬到第i级需要花费cost[i-2]并且选择爬两层楼梯
或者已经爬到了第i-1层,那么爬到第i层需要花费cost[i-1]并且选择爬1层楼梯
此时我们只需比较选择花费较小的那个就行了.
d p [ i ] = m i n ( d p [ i − 2 ] + c o s t [ i − 2 ] , d p [ i − 1 ] + c o s t [ i − 1 ] ) ; dp[i] = min(dp[i-2]+cost[i-2],dp[i-1]+cost[i-1]); dp[i]=min(dp[i−2]+cost[i−2],dp[i−1]+cost[i−1]); - d p [ 0 ] = 0 ; d p [ 1 ] = 0 ; dp[0] = 0; dp[1] = 0; dp[0]=0;dp[1]=0;
- 由递推公式确定我们是从前往后遍历
- 检查,我没检查,当时debug弄了半天,不过不是思路的事情,想递归公式的过程是正面执行的,示例一帮了我很大忙.
过了!很爽!因为是自己想的.
并查集
- p[i] 的含义是,节点 i 所属集合的老大. // 通过老大来给这个集合起一个名字.
- 如果 p[i] = i,那么它 (i) 就是老大
- 代码
int p[N];
int find(int x)
{
if(p[x]!=x) p[x] = find(p[x]);
return p[x];
}
//初始化
for(int i = 1;i<N;i++) p[i] = i;
- 易错点:Kruskal算法用并查集写,而不是 “标记法” – 开一个数组,访问过记为1,没访问过记为 0.(标记法已经用实例鉴定为:寄!)
可图性判断(Havel-Hakimi定理)
- 题目描述: 给一个 度 序列, 判断它是否能画出简单无向图
- 我叫他:“哈哈定理”
- 哈哈步骤:
- 排序,从高到低
- 第一个数为 x , 那么后面x个数-1, 再排序
- 反复重复,直到全为零 — OK, 出现负数 — 不能画出简单无向图
- 举例: 5 4 3 2 1
… 3 2 1 0 — … 3 2 1 0
… … 1 0 -1 — 出现负数直接return false. - 代码: (这个是接用CSDN用户"CAPTAIN船长"的代码,我能看懂觉得很好,仅作为个人学习使用)
bool Havel_Hakimi(int arr[]){
for(int i=0; i<n-1; ++i)
{
sort(arr+i,arr+n);// 从第i个元素开始非递增排序
if(i+arr[i] >= n) return false;//若第i个元素+arr[i]的值超过原数组长度,那么将溢出。
for(int j=i+1; j<=i+arr[i] ; ++j)
{
--arr[j];
if(arr[j] < 0) return false;
}
}
if(arr[n-1]!=0) return false;
return true;
}