DP入门

Posted honey-cat

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DP入门相关的知识,希望对你有一定的参考价值。

  • 解决最优化问题 -- 多阶段决策

  • pull: f(x)从之前的状态得到(数据不规则,更好用)

  • push:对于每个f(x),更新f(x)可以到达的所有位置的结论


  • 使用条件:

    大问题可以拆成若干小问题

    1. 无后效性 --> 与之前状态计算过程无关(只需要结果)"未来与过去无关"

    2. 最优子结构

    若后效性不好消除 -- dp数组加一维 -- 消除后效性

  • 在最大可解区间中,寻找最优解(贪心将可解空间降到了最小),尽量缩小可解空间

  • DP舍弃了许多不会成为最优解的答案 -- 自带剪枝

  • 大事化小,小事化了: 大问题 - 小问题 - 求解

当前状态为x,设计f(x):状态转移方程(看与那些局面有关)

DP三连:
  • 我是谁? : 设计状态,表示局面
  • 我从哪来? 我要到哪里去? : 设计转移

POJ: The Triangle

//三角形中,每个节点可以更新左下(i+1,j)与右下(i+1,j+1) 两个点
//每个节点可以被左上(i - 1,j - 1)和右上(i - 1,j)两点更新
pull:dp[i][j] = max(dp[i - 1][j - 1],dp[i - 1][j]) + dp[i][j];
push:dp(1,1) = w(1,1)
     dp[i + 1][j] = max(dp[i + 1][j],dp[i][j] + w[i + 1][j]);
     dp[i + 1][j + 1] = max(dp[i + 1][j + 1],dp[i][j] + w[i + 1][j + 1]);
//更新dp数组之后,遍历最后一行,输出最大值
记忆化搜索
  • 斐波那契
int fib[N];

int cal(int idx){
	if(idx == 1 || idx == 2) return 1;
	if(fib[idx]) return fib[idx];

	return cal(idx - 1) + cal(idx - 2);
}
  • 洛谷P1464 Function
const int N = 25;
int a,b,c,f[N][N][N];
int cal(int i,int j, int k){
	if(!i || !j || !k){
		return 1;
	}
	if(f[i][j][k] != 0) return f[i][j][k];
	if(a < b && b < c){
		return cal(i,j,k - 1) + (i,j - 1,k -1) - cal(i,j - 1,k);
	}
	return cal(i - 1,j,k) + cal(i - 1,j - 1,k) + cal(i - 1,j,k -1) - cal(i -1,j -1,k -1);
}
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);

	for(int i = 0; i <= 20; i++){
		for(int j = 0; j <= 20; j++){
			for(int k = 0; k <= 20; k++){
				f[i][j][k] = cal(i,j,k);
			}
		}
	}
	
	while(cin >> a >> b >> c){
		if(a == -1 && b == -1 && c == -1) break;
		cout << "w(" << a <<", " << b << ", " << c << ") = ";
		if(a <= 0 || b <= 0 || c <= 0){
			cout << f[0][0][0] << endl;
		}else if(a > 20 || b > 20 || c > 20) cout << f[20][20][20] << endl;
		else cout << f[a][b][c] << endl; 
	}
	return 0;
}
计数DP
  • 数楼梯 -- pull型好写
  • 过河卒问题 //dp(x,y) 到 x,y的路径条数
洛谷P1049 装箱问题
//滚动数组 数据值与前一层有关 -- 可大幅优化空间复杂度 -- 节省一维空间
//相当于01背包,将价值等于体积
int v,n,t,f[N];//f(x) 物品总体积为x时最大价值 
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin >> v >> n;
	for(int i = 0; i < n; i++){
		cin >> t;
		for(int j = v; j >= t; j--){
			f[j] = max(f[j],f[j - t] + t);
		}
	}
	cout << v - f[v];
	return 0;
}
洛谷P1048 采药
int x,y,t,m,f[N];//n时间内,最大价值 
//思路同上
洛谷P1541 乌龟棋
//四种卡片,四个条件
f[i][j][k][l] = max(f[i][j][k][l],max(f[i - 1][j][k][l],max(f[i][j - 1][k][l],max(f[i][j][k - 1][l],f[i][j][k][l - 1]))))//可以不选 or 选四种卡片之一

以上是关于DP入门的主要内容,如果未能解决你的问题,请参考以下文章

1024. 视频拼接 dp

插头DP入门

dfs与dp算法之关系与经典入门例题

HDU 2089 数位dp入门

POJ3691DNA repair(AC自动机,DP)

HDU1520 Anniversary party(树形dp入门题)