动态规划入门练习

目录

1.逃生

2.一维消消乐

3.数组分组

4.墙壁涂色

5.过河


1.逃生

蒜头君在玩一款逃生的游戏。在一个 n×m 的矩形地图上,蒜头位于其中一个点。地图上每个格子有加血的药剂,和掉血的火焰,药剂的药效不同,火焰的大小也不同,每个格子上有一个数字,如果格子上的数字是正数说明是一个药剂代表增加的生命值,如果是负数说明是火焰代表失去的生命值。

蒜头初始化有 v 点血量,他的血量上限是 c,任何时刻他的生命值都不能大于血量上限,如果血量为 0 则会死亡,不能继续游戏。

矩形地图上的四个角(1,1),(1,m),(n,1),(n,m)为游戏的出口。游戏中只要选定了一个出口,就必须朝着这个方向走。例如,选择了左下的出口,就只能往左和下两个方向前进,选择了右上的出口,就只能往右和上两个方向前进,左上和右下方向的出口同理。

如果成功逃生,那么剩余生命值越高,则游戏分数越高。为了能拿到最高分,请你帮忙计算如果成功逃生最多能剩余多少血量,如果不能逃生输出 −1。
输入格式

第一行依次输入整数 n,m,x,y,v,c(1<n,m≤1000,1≤x≤n,1≤y≤m,1≤v≤c≤10000), 其中 n,m 代表地图大小,(x,y) 代表蒜头君的初始位置,v 代表蒜头的初始化血量,c 代表蒜头的生命值上限。

接下来 n 行,每行有 m 个数字,代表地图信息。(每个数字的绝对值不大于100,地图中蒜头君的初始位置的值一定为 0)
输出格式

一行输出一个数字,代表成功逃生最多剩余的血量,如果失败输出 −1。

样例输入

4 4 3 2 5 10

1 2 3 4

-1 -2 -3 -4

4 0 2 1

-4 -3 -2 -1

样例输出

10

#include<bits/stdc++.h>
using namespace std;
int n,m,x,y,v,c;
int dirx[4] = {-1,-1,1,1};
int diry[4] = {-1,1,-1,1};
int a[1010][1010];
int dp[1010][1010];
int INF = 0x3f3f3f3f;
int main()
{
	cin>>n>>m>>x>>y>>v>>c;
	for(int i = 1;i<=n;i++){
		for(int j = 1;j<=m;++j){
			cin>>a[i][j];
		}
	}
	for(int t = 0;t<4;t++){
		for(int i = x;i>0&&i<=n;i-=dirx[t]){
			for(int j = y;j>0&&j<=m;j-=diry[t]){
				if(i == x && j == y) dp[i][j] = v;
				else if(i == x) dp[i][j] = min(c,dp[i][j+diry[t]] + a[i][j]);
				else if(j == y) dp[i][j] = min(c,dp[i+dirx[t]][j] + a[i][j]);
				else dp[i][j] = min(c,max(dp[i+dirx[t]][j],dp[i][j+diry[t]]) + a[i][j]);
				if(dp[i][j] <= 0) dp[i][j] = -INF;
			}
		}
	}
	int ans = max(max(dp[1][1],dp[1][n]),max(dp[n][1],dp[n][n]));
	if(ans<=0) cout<<"-1"<<endl;
	else cout<<ans<<endl;
	return 0;
}

2.一维消消乐

一维消消乐是一款非常简单的游戏。有 n 颗珠子排成一排,每一颗珠子有一个价值 wi(可能是负数)。游戏是这样,你可以选择如若干对相邻的珠子,让他们同时消去。每一对珠子的消失,都会使得总分数加上两颗珠子相乘的分数。注意,每个珠子只能消一次,并且珠子消去以后,还会占位。
输入格式
输入第一行一个整数n(1≤n≤10000)。
接下来一行输入 n 个整数  (−1000≤ wi ≤1000)。
输出格式
输出最大的分数。
样例输入:

8
-9 -5 -4 -2 4 -5 -4 2
样例输出:

73

#include<bits/stdc++.h>
using namespace std;
int n;
int dp[10010][2];
int a[10010];
int main()
{
	cin>>n;
	for(int i = 1;i<=n;i++){
		cin>>a[i];
	}
	dp[1][0];
	for(int i = 2;i<=n;i++){
		dp[i][0] = max(dp[i-1][0],dp[i-1][1]);
		dp[i][1] = dp[i-1][0] + a[i]*a[i-1];
	}
	cout<<max(dp[n][0],dp[n][1])<<endl;
	return 0;
}

3.数组分组

一个长度为 n 的数组 a,我们可以把它分成任意组,每一组是一段连续的区间。比如数组1,2,3,4,5 可以分成 (1,2),(3,4,5) 两组。每个分组都有一个权值,这个权值就是分组里面每个数的乘积对1000 取模的结果。对于数组 a 的一个分组方案,总权值就是每个分组的权值和。那么对于数组 a,分组以后最大的权值和是多少?

输入格式

输入第一张一个整数 n(1≤n≤1000)。
接下来一行 n 个整数,表示数组 a,数组中每个元素都小于等于 100。

输出格式

数组最大的分组权值和。

样例输入:

7
52 26 1 36 72 48 43

样例输出:

1596

#include<bits/stdc++.h>
using namespace std;
int n;
int a[1005],pre[1005][1005],dp[1005];
int main()
{
	cin>>n;
	for(int i = 1;i<=n;i++){
		cin>>a[i];
	}
	for(int i = 1;i<=n;i++){
		pre[i][i] = a[i];
		for(int j = i+1;j<=n;j++){
			pre[i][j] = pre[i][j-1]*a[j] %1000;
		}
	}
	dp[0] = 0;
	dp[1] = a[1];
	for(int i = 2;i<=n;i++){
		for(int j = 0;j<i;j++){
			dp[i] = max(dp[i],dp[j]+pre[j+1][i]);
		}
	}
	cout<<dp[n]<<endl;
	return 0;
}

4.墙壁涂色

蒜头君觉得白色的墙面好单调,他决定给房间的墙面涂上颜色。他买了 3 种颜料分别是红、黄、蓝,然后把房间的墙壁竖直地划分成 n 个部分,蒜头希望每个相邻的部分颜色不能相同。他想知道一共有多少种给房间上色的方案。
例如,当 n=5 时,下面就是一种合法方案。
|蓝|红|黄|红|黄|
由于墙壁是一个环形,所以下面这个方案就是不合法的。
|蓝|红|黄|红|黄|蓝|
输入格式
一个整数 n,表示房间被划分成多少部分。(1≤n≤50)
输出格式
一个整数,表示给墙壁涂色的合法方案数。
样例输入
4
样例输出
18

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	cin>>n;
	long long a[51];
	a[1] = 3;
	a[2] = 6;
	a[3] = 6;
	for(int i = 4;i<=n;i++){
		a[i] = a[i-1] + a[i-2]*2;
	}
	cout<<a[n]<<endl;
	return 0;
}

5.过河

在一个夜黑风高的晚上,有n个小朋友在桥的这边,现在他们需要过桥,但是由于桥很窄,每次只允许不超过俩个人通过,他们只有一个手电筒,所以每次过桥后,需要有人把手电筒带回来,第i号小朋友过桥的时间为a【i】,俩个人过桥的总时间为二者中最长的。问所有小朋友过桥的总时间最短是多少。

输入格式

第一行输入一个整数n(1<n<=1000),表示n个小朋友。

第二行有n个整数ai,表示第i个小朋友过河的时间。

输出格式

输出一个整数,表示所有小朋友过河所需要的时间。

样例输入

4

1 2 5 10

样例输出

17

#include<bits/stdc++.h>
using namespace std;
int a[1005],dp[1005];
int main()
{
	int n;
	cin >> n;
	for(int i = 0;i < n;i++) cin >> a[i];
	sort(a,a + n);
	dp[0] = a[0];
	dp[1] = a[1];
	for(int i = 2;i < n;i++) dp[i] = min(dp[i - 1] + a[0] + a[i],dp[i - 2] + a[0] + 2 * a[1] + a[i]);
	cout << dp[n - 1] << endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值