DP总结

Normal \(\text{dp}\)

1. 区间

思路

枚举长度起点终点

板子

样题:石子合并

for(int len = 2; len <= n; len++)//枚举长度
	for(int i = 1; i+len-1 <= n; i++){//枚举起点
		int j = i+len-1;//算出终点
                d[i][j] = inf;//若求最小值,建议在此处初始化,避免不明错误。
		for(int k = i; k < j; k++){
			d[i][j] = min(d[i][k], d[k+1][j]);//最大最小值等dp操作
		}
	}

2. 分组

思路

设置 \(d[n]\) 数组,为\(n\) 个数据分组结果的最大最小值等。

板子

样题:任务安排

for(int i = 1; i <= n; i++){
	d[i] = inf;//初始化最小值
	for(int j = 1; j <= i; j++){//枚举上一组(起点)
		d[i] = min(d[i], d[j-1] + s*(f[n] - f[j-1]) + t[i]*(f[i] - f[j-1]));
	}
}

3. 状压

思路

优雅的暴力——使用二进制(有的题目可能涉及到3维4维)储存状态
\(d[state]\) 存储 \(d_{k_1}+d_{k_2}+d_{k_3}+···+d_{k_n}\)
例如:开关灯中, \(d[101101100(二进制)]\) 可存储1号、3号、4号、6号、7号(二进制中是1的位置)的灯被打开时的状态

板子

样题:摩天大厦里的奶牛

memset(d, 63, sizeof(d));
g[0] = w; d[0] = 1;//初始化
for(int i = 0; i <= (1<<n)-1; i++){//枚举状态
	for(int j = 1; j <= n; j++){
		int now = (i | 1<<(j-1));
		if(i & 1<<(j-1)) continue;
		if(g[i] >= a[j] && d[now] >= d[i]){
			d[now] = d[i];
			g[now] = max(g[now], g[i] - a[j]);
		}
		else if(g[i] < a[j] && d[now] >= d[i]+1){
			d[now] = d[i] + 1;
			g[now] = max(g[now], w - a[j]);
		}
	}
}

4. 背包

背包问题属于 \(\text{dp}\) 入门题型,老生常谈了,在此分享一道有趣的题目。

思路

背包问题主要是设置 \(d[c]\),用容量进行枚举。

题目(板子)

题面:奶牛展览G
\(0/1\) 背包变形,有两个量,选择其一作为容量进行 \(\text{dp}\)

memset(d, -63, sizeof(d));
d[400000] = 0;//因为有负数,所以开两倍数组容量进行正负数处理
for(int i = 1; i <= n; i++){
	if(f[i] >= 0)//正
		for(int j = 800000; j >= f[i]; j--){
			d[j] = max(d[j], d[j-f[i]] + s[i]);
		}
	else //负
		for(int j = 0; j <= 800000+f[i]; j++){
			d[j] = max(d[j], d[j-f[i]] + s[i]);
		}
}

树形 \(\text{dp}\)

1. 基础

思路

设置一个 \(d[n][k]\)\(\text{dp}\) 数组,数组中存储本身及其子树的最大最小值等,\(k\) 用于分情况

板子

样题:没有上司的舞会

void dfs(int x){
	d[x][1] = r[x];//题目情况需求
	for(int i = first[x]; i; i = a[i].Next){
		int To = a[i].to;
		dfs(To);//深度优先先走到底
		d[x][0] += max(d[To][1], d[To][0]);
		d[x][1] += d[To][0];//最大最小值等dp操作
	}
}

2. 背包

思路

背包问题的思想在树上选择点进行 \(\text{dp}\)

板子

样题:有线电视网

void dfs(int x){
	if(x > n-m){ //题目情况需求
		d[x][1] = v[x];
		sz[x] = 1;
		return ;
	}
	for(int i = first[x]; i; i = a[i].Next){ //枚举组==树的遍历
		int To = a[i].to;
		dfs(To);
		sz[x] += sz[To];
		for(int j = sz[x]; j > 0; j--){ //枚举"背包容量"
			for(int l = 1; l <= sz[To] && l <= j; l++){ //枚举选择的"物品"(点)
				d[x][j] = max(d[x][j], d[x][j-l] + d[To][l] - a[i].c);
			}
		} 
	}
}

经典(难)题

1. 魔兽地图

posted @ 2020-05-25 17:37  Michmh  阅读(170)  评论(1)    收藏  举报