【动态规划】入门练习题浅总

1、入门

(1) 摘花生

【题目描述】Hello Kitty想摘

点花生送给她喜欢的米老鼠。她来到一片有网格状道路的矩形花生地(如右图),从西北角进去,东南角出来。

地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。Hello
Kitty只能向东或向南走,不能向西或向北走。

问Hello Kitty最多能够摘到多少颗花生。
在这里插入图片描述

【输入格式】
第一行是一个整数T,代表一共有多少组数据。

接下来是T组数据。

每组数据的第一行是两个整数,分别代表花生苗的行数R和列数 C。

每组数据的接下来R行数据,从北向南依次描述每行花生苗的情况。每行数据有C个整数,按从西向东的顺序描述了该行每株花生苗上的花生数目M。

【输出格式】 对每组输入数据,输出一行,内容为Hello Kitty能摘到得最多的花生颗数。

样例
input
2
2 2
1 1
3 4
2 3
2 3 4
1 6 5
output
8
16

【分析】这道题作为dp的入门题,非常的简单啊!因为HK只能向右或向下,有明显顺序,那么dp的阶段就可以是HK的位置。我们显然知道HK位于点 [ i ] [ j ] [ i ][ j ] [i][j]时所摘到的花生的最大值只和 [ i − 1 ] [ j ] [ i-1 ][ j ] [i1][j] [ i ] [ j − 1 ] [ i ][ j-1 ] [i][j1]有关。可以用表示Hk位于点[ i ][ j ]时所摘到的花生的最大值。
状态转移方程为 f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , [ i ] [ j − 1 ] ) + a [ i ] [ j ] f[ i ][ j ]=max( f[ i-1 ][ j ] ,[ i ][ j-1 ] )+a[ i ][ j ] f[i][j]=max(f[i1][j],[i][j1])+a[i][j]。( a [ i ] [ j ] a[ i ][ j ] a[i][j] 表示每个位置本身的花生数),本题得到解决。

具体代码如下:

#include<bits/stdc++.h>
using namespace std;
int t,r,c;
int a[110][110],f[110][110];
void Pick_up(){
   
	f[1][1]=a[1][1];
	for(int i=1;i<=r;i++)
	    for(int j=1;j<=c;j++)
	        f[i][j]=max(f[i][j-1],f[i-1][j])+fd[i][j];
	printf("%d\n",f[r][c]);
}
int main(){
   
	scanf("%d",&t);
	for(int i=1;i<=t;i++){
   
		memset(a,0,sizeof(a));
		memset(f,0,sizeof(f));
		scanf("%d%d",&r,&c);
		for(int i=1;i<=r;i++)
		    for(int j=1;j<=c;j++)
		        scanf("%d",&a[i][j]);
		Pick_up();
	}
}

(2)黑熊过河

【题目描述】晶晶的爸爸给晶晶出了一道难题:有一只黑熊想过河,但河很宽,黑熊不会游泳,只能借助河面上的石墩跳过去。

它可以一次跳一墩,也可以一次跳两墩,但是每跳一次都会耗费一定的能量,黑熊最终可能因能量不够而掉入水中。所幸的是,有些石墩上放了一些食物,一些食物可以给黑熊增加一定的能量。问黑熊能否利用这些石墩安全地抵达对岸?请计算出抵达对岸后剩余能量的最大值。
【输入格式】
第1行包含两个整数和(0≤≤1000),其中P表示黑熊的初始能量,Q表示黑熊每次起跳时耗费的能量。

第2行只有一个整数(1≤≤10^6),表示河中石墩的数目。

第3行有个整数,表示每个石墩上食物的能量值(0≤≤1000)。

【输出格式】
输出1行,若黑熊能抵达对岸,输出抵达对岸后剩余能量的最大值;若不能,则输出“NO”。

【样例数据】
input

12 5
5
0 5 2 0 7
output
6

【分析】这道题也是非常的简单啊! 看到“跳一墩”、“跳两墩”这样的字样,我们一定会想到之前学习的递归、递推、记忆化搜索。一般dp选用递推或记忆化搜索。可以以黑熊的位置划分阶段。用 f [ i ] f[ i ] f[i]表示黑熊位于点i时的剩余能量最大值。

状态转移方程为: f [ i ] = m a x ( f [ i − 1 ] , f [ i − 2 ] ) − q + a [ i ] f[ i ]=max(f[ i-1 ] , f[ i-2 ])-q+a[i] f[i]=max(f[i1],f[i2])q+a[i]。 本题得到解决。

具体代码如下:

#include<bits/stdc++.h>
using namespace std;
int p,q,n,a[1000010],f[1000010];
int main(){
   
	scanf("%d%d%d",&p,&q,&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	dp[0]=p; dp[1]=p-q+a[1];
	for(int i=2;i<=n+1;i++){
   
		if(f[i-1]<q&&f[i-2]<q){
   
			cout<<"NO"<<endl;
			return 0;//连续两个为负黑熊就gg了
		}
		f[i]=max(f[i-1],f[i-2])-q+a[i];//状态转移
	}
	cout<<f[n+1]<<endl;
}

2、要素与动机

(1)[USACO06JAN]Dollar Dayz S

【题目描述】FJ 到商场买工具。商场里有 K 种工具(1≤K≤100),价格分别为 1,2,…,K 元。FJ 手里有 N 元(1≤N≤1000),必须花完。他有多少种购买方案呢?
【输入格式】 一行两个整数 。

【输出格式】 输出不同的购买方案数。

【样例数据】
input
5 3
output
5

【分析】题目描述很简单,但根据我们小学二年级就学过的真理,我们可以知道,简单的题往往会在你没注意到的地方痛击你。根据dp求什么设什么的普遍思路,我们可以设 f [ i ] f[ i ] f[i]为花i元钱的选择数,则 f [ i ] = f [ i ] + f [ i − j ] f[ i ]=f[ i ]+f[ i-j ] f[i]=f[i]+f[ij]。本题得到解决。。。等等,为什么只得了 70 70 70分。再看数据规模,不仅爆 i n t int int l o n g long long l o n g long long也会爆掉。所以想要解决本题,需要用到高精度。(集训完一定补 Q A Q QAQ QAQ

70 70 70分无高精度代码如下:

#include<bits/stdc++.h>
using namespace std;
long long f[1010],k,n;
int main(){
   
    cin>>n>>k;//总共n元,k种选择 
    f[0]=1;
	for(int i=1;i<=k;i++)
	    for(int j=i;j<=n;j++)
	        f[j]=f[j]+f[j-i];
	cout<<f[n]<<endl;
    return 0;
}

做了这么多道题,我们对dp已经有了一个初步的了解,当然这只是冰山一角。

dp的基本做题流程为:

1.划分阶段 一般以时间、位置等某种题目已知元素为阶段

2.通过阶段确定状态表达 即f[i][j]表示什么。

3.找出已知状态,初始化。

4.写出状态表达方程

5.找出答案

(2)[递推练习]石头剪刀布

【题目描述】小菜给出一个自然数n,小明和小头轮流操作,每次操作可以把n减去n所拥有的数字中的最大值或者最小值(不包括0),不能操作者输(即当n=0时)。小菜怕他们不懂,于是给了一个例子n=1024,则最大值=4,最小值=1。假如小明先操作,那么他可以留给小头1023或者1020。小明和小头觉得这个游戏很好玩,于是打算多玩几局。每次由小明先操作,如果两人都是用最佳策略,请问谁会赢?
【输入格式】 第一行只有一个整数t,表示玩t轮游戏 接下来t行,每行一个自然数n,表示初始时小菜给出的自然数。 【输出格式】
共t行,如果小明赢了第i轮游戏则在第i行输出“YES” 否则输出“NO” 【样例数据】
input
2
9
10
output
YES
NO

【分析】本题涉及点博弈论,但是我们不关心。所谓的最佳策略就是在自己有赢的可能的情况下让自己赢,使原来的概率事件变为必然事件,因此可以用一个bool数组记录游戏结果。

f [ i ] f[ i ] f[i] 表示数字为i时能否胜利。由题意易知 f [ 0 ] = 1 f[0]=1 f[0]=1,且当 i i i为个位数时, f [ i ] f[ i ] f[i]的值都为 0 0 0

我们只需要额外记录一下查询数的最大最小值 m a x x 、 m i n n maxx、minn maxxminn就可以了。

状态转移方程: f [ i ] = ! f [ i − m i n n ] ∣ ∣ ! f [ i − m a x x ] f[ i ]=!f[ i-minn ]||!f[ i-maxx ] f[i]=!f[iminn]∣∣!f[imaxx](只要 i − m i n n i-minn iminn i − m a x x i-maxx imaxx中有一个为0,那么

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值