luogu 【动态规划1】动态规划的引入

P1216 数字三角形

每个节点的值只受左上,右上两节点影响。索引从1开始,避免处理边界问题。

int n,ans,a[1005][1005],dp[1005][1005];
//pull: dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + dp[i][j]; 
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> n;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= i; j++) cin >> dp[i][j];
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= i;j++) dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + dp[i][j];
	}
	for(int i = 1; i <= n; i++) ans = max(ans, dp[n][i]);
	cout << ans;	
	return 0;
}
P1434 滑雪

一个二维数组,每个数字代表当前点的高度,可以上下左右移动且只可以由高的点移动到低的点,找出最大移动次数 -- 记忆化搜索。

int n,m,ans,d[110][110],h[110][110],dir[2][4] = {-1,1,0,0,0,0,-1,1};
//d[x][y]记录(x,y)的最大移动次数 
int dfs(int x,int y){
	if(d[x][y]) return d[x][y];
	d[x][y] = 1;//最小下滑次数为1 
	for(int i = 0; i < 4; i++){
		int tx = x + dir[0][i];
		int ty = y + dir[1][i];
		if(h[tx][ty] > h[x][y]) d[x][y] = max(d[x][y], dfs(tx, ty) + 1);
	}
	return d[x][y];
}
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++) cin >> h[i][j];
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++) ans = max(ans, dfs(i,j));
	}	
	cout << ans;
	return 0;
}
P2196 挖地雷

n个地窖,每个地窖中有若干个地雷,地窖间的连接情况已给出,设计一个挖出地雷最多的方案。

  • n <= 20,可以直接暴力...
  • 思路同最长上升子序列,dp[i]表示以i为终点时挖到的最大地雷数
  • 枚举i之前的所有的地窖,与i连接时,判断是否可以通过这个地窖挖取更多的地雷,pre数组记录各节点的前驱,更新dp数组的同时,更新相应的前驱。
int n,m,ans,t,idx,dp[110],link[25][25],w[25],pre[25];
void print(int i){
	if(pre[i]) print(pre[i]);
	cout << i << ' ';
}
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> w[i];
	for(int i = 1; i <= n - 1; i++){
		for(int j = i + 1; j <= n; j++) cin >> link[i][j];
	}
	dp[1] = w[1];
	for(int i = 2; i <= n; i++){
		dp[i] = w[i];
		for(int j = 1; j < i; j++){
			if(link[j][i] && dp[i] < dp[j] + w[i]){
				dp[i] = dp[j] + w[i];
				pre[i] = j;
			}
		}
		if(dp[i] > ans){
			ans = dp[i];
			t = i;
		}
	}
	print(t);
	cout << endl << ans;
	return 0;
}
P4017 最大食物链计数

n类生物,m种关系,求最大食物链数量mod 80112002的值。n个点组成了有m条边的DAG --> 入度为0的点到出度为0的点之间路径的个数 --> 可以用拓扑排序 or 记忆化搜索

  • 拓扑排序:
    1. num数组记录各节点权值,初始入度为0的点权值为1,删除这些节点时,将权值加到所有相邻的节点上。
    2. 排序结束后,统计出度为0的节点的权值和
int n,m,a,b,idx,ans,in[5005],out[5005],h[5005],num[5005];//in out 记录个点出度 入度 
const int mod = 80112002, kN = 5e5 + 5;
struct node{
	int e,ne;
}edges[kN];
void add(int a, int b){//s -> e
	edges[idx].e = b;
	edges[idx].ne = h[a];
	h[a] = idx++;
}
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	memset(h, -1, sizeof h);
    
	cin >> n >> m;
	for(int i = 0; i < m; i++){
		cin >> a >> b;
		add(a, b);
		++out[a];
		++in[b];
	}
	queue<int> q;
	for(int i = 1; i <= n; i++){
		if(in[i] == 0){//入度为0的点入队 
			num[i] = 1,q.push(i); 
		}
	}
	while(!q.empty()){
		int top = q.front();
		q.pop();
		//删除top,相邻点入度-1 
		for(int i = h[top]; i != -1; i = edges[i].ne){
			--in[edges[i].e];
			num[edges[i].e] = (num[edges[i].e] + num[top]) % mod;
			if(in[edges[i].e] == 0) q.push(edges[i].e);  
		}  
	}
	//搜索出度为0的点,计算答案 
	for(int i = 1; i <= n; i++){
		if(!out[i]) ans = (ans + num[i]) % mod;
	}
	cout << ans;
	return 0;
}
  • 记忆化搜索
    1. 入度为0的节点:起点,出度为0的节点:终点
    2. 搜索到终点即为一条最长食物链。
    3. dp[i]表示i到终点有多少条路径,搜索时存在dp[i],返回相应值
    4. 搜索所有的起点,结果求和取模。
P1048 采药

裸01背包,dp(i,j)表示前i株药材采摘耗时为j的最大价值。由于i状态只与i - 1状态有关,可使用滚动数组优化,dp[i]表示耗时为i时的最大价值.

#include<iostream>
#include<algorithm>
#define endl '\n'
using namespace std;
int n,m,ans,dp[1010],t,w;
//int t[110],w[110];
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> n >> m;
	for(int i = 0; i < m; i++){
		cin >> t >> w;
		for(int j = n; j >= t; j--){//大-小枚举,只可使用一次 
			dp[j]  = max(dp[j], dp[j - t] + w);
		}
	}
	cout << dp[n];
	/*
	for(int i = 1; i <= m; i++) cin >> t[i] >> w[i];
	
	for(int i = 1; i <= m; i++){
		for(int j = 0; j <= n; j++){
			dp[i][j] = dp[i - 1][j];//第i间物品有选 or 不选两种状态
			if(j >= t[i]) dp[i][j] = max(dp[i][j], dp[i - 1][j - t[i]] + w[i]); 
		}
	}
	cout << dp[m][n];*/
	
	return 0;
}
P1616 疯狂的采药

完全背包问题,滚动数组优化的01背包中时间从小到大枚举即可。

	for(int i = 0; i < m; i++){
		cin >> t >> w;
		for(int j = t; j <= n; j++) dp[j] = max(dp[j], dp[j - t] + w);
	}
P1802 五倍经验日

01背包...,每一个好友都有输 or 赢两种状态,dp[j]表示消耗j个药物获得的最大经验

  • j >= use,win or lose : dp[j] = max(dp[j] + lose, dp[j - use] + win);
  • j < use, lose: dp[j] += lose;
  • 存在use == 0的情况
int n,x,lose,win,use;
long long dp[10010];
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> n >> x;
	for(int i = 1; i <= n; i++){
		cin >> lose >> win >> use;
		for(int j = x; j >= use; j--) dp[j] = max(dp[j] + lose, dp[j - use] + win);
		for(int j = use - 1; j >= 0; j--) dp[j] += lose;
	}
	cout << dp[x] * 5;
	return 0;
}
P1002 过河卒
//A(0,0) --> B(n, m)的路径条数,其中马所控制的八个点不可以走
//dp[x][y]表示到达x,y点的路径数;
//由于卒只能向下向右走:dp[x][y] = dp[x - 1][y] + dp[x][y - 1]
int n,m,hx,hy,dir[4] = {1,-1,2,-2};
long long dp[25][25];
bool book[25][25];
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> n >> m >> hx >> hy;
	book[hx][hy] = true;
	for(int i = 0; i < 4; i++){
		for(int j = 0; j < 4; j++){
			if(i == j) continue;
			if(abs(dir[i]) == abs(dir[j])) continue;
			int tx = hx + dir[i];
			int ty = hy + dir[j];
			book[tx][ty] = true;
		}
	}
	for(int i = 1; i <= n && !book[i][0]; i++) dp[i][0] = 1;
	for(int i = 1; i <= m && !book[0][i]; i++) dp[0][i] = 1; 
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			if(!book[i - 1][j]) dp[i][j] +=dp[i - 1][j];
			if(!book[i][j - 1]) dp[i][j] += dp[i][j - 1];
		}
	}
	cout << dp[n][m];
	return 0;
}
  • 滚动数组优化,待填坑...
posted @ 2020-06-21 18:04  jimmy-cat  阅读(184)  评论(0编辑  收藏  举报