DP2补题报告

题目来自可达鸭

1.大盗阿福

时间限制:1秒        内存限制:128M

题目描述

阿福是一名经验丰富的大盗。趁着月黑风高,阿福打算今晚洗劫一条街上的店铺。 

这条街上一共有 N 家店铺,每家店中都有一些现金。阿福事先调查得知,只有当他同时洗劫了两家相邻的店铺时,街上的报警系统才会启动,然后警察就会蜂拥而至。 

作为一向谨慎作案的大盗,阿福不愿意冒着被警察追捕的风险行窃。他想知道,在不惊动警察的情况下,他今晚最多可以得到多少现金?

输入描述

输入的第一行是一个整数T(T≤50) ,表示一共有T组数据。 

接下来的每组数据,第一行是一个整数N(1≤N≤100,000) ,表示一共有N家店铺。第二行是N个被空格分开的正整数,表示每一家店铺中的现金数量。每家店铺中的现金数量均不超过1000。

输出描述

对于每组数据,输出一行。该行包含一个整数,表示阿福在不惊动警察的情况下可以得到的现金数量。

样例

输入

2
3
1 8 2
4
10 7 6 14

输出

8
24

提示

对于第一组样例,阿福选择第2家店铺行窃,获得的现金数量为8。 

对于第二组样例,阿福选择第1和4家店铺行窃,获得的现金数量为10+14=24。

思路解析:

此时有两个方法。第一个方法在不考虑时间超不超限的情况下,可以用双层for循环。进行一个判断,就是假设我拿这一个,那我剩下拿一个我求一个max就是最大的,因为它要求最大值就直接去求。第二个方法就是这里的ac代码。思想更简单,就是首先初始化是一定的,初始化肯定是第一批一等于1dp2,就是a[1]大还是a[2]大,拿这两个店铺中的任意一个取最大值。然后你去遍历所有店铺求最大值,是因为每一个这个店铺从第三个店铺开始,他每一个店铺他无非就是两种情况,从上两个来或从上三个来。也就是说从上两个来多还是从上三个来读,求一个最大值,加上这个店铺抢劫抢出来的总钱数就是呃dpi这个位置的总钱数,最后一定要注意的就是判断最后一个和最后一个的前一个。

AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int t,n,dp[100005],a[100005];
int main(){
	cin>>t;
	while(t--){
		cin>>n;
		memset(dp,0,sizeof(dp));
		for (int i=1;i<=n;i++){
			cin>>a[i];
		}
		dp[1]=a[1];
		dp[2]=max(a[1],a[2]);
		for (int i=3;i<=n;i++){
			dp[i]=max(dp[i-2],dp[i-3])+a[i];
		}
		cout<<max(dp[n],dp[n-1])<<endl;
	} 
	return 0;
} 

2.股票买卖II

时间限制:1秒        内存限制:128M

题目描述
给定一个长度为N的数组,数组中的第i个数字表示一个给定股票在第i天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
输入描述
第一行包含整数N, 表示数组长度。

第二行包含N个不大于 10000 的正整数,表示完整的数组。
输出描述
输出一个整数,表示最大利润。
数据范围
1 <= N <=10^5​​
样例输入

6
7 1 5 3 6 4

样例输出

7

思路解析:

这个题目就是去求他最后炒股能拿到多少钱的一个问题。他说不能同时交易,也就是说什么呢?就是你手上有股票你就不能买,你只有把这个股票卖出去了,你才能再买,因为他只有一只股票。那我们的思想是什么呢?非常简单,就是在价格低的时候买入,在价格高的时候卖出。这就是一个非常简单的一个思想。我那究竟该怎么做呢?实际上更简单,就是我们先设计一个DP数组。实际上DP最难的就是在设计上。Ac代码中这个dp数组的含义就是前爱天状态为j的最大钱数。那这个j是什么呢?,就是0或者1。为零的时候状态为零。也就说手上未持物,当状态为一的时候,手上就持股了。当你第二天你持股了会发生两种情况。一种是你前一天手上未持股,你今天买了这只股票了。第二种就是你前一天就已经持股了。那当你未持股的时候,那就也会有两种情况。第一就是你前一天我也未持股。就是你前一天已持股了,你今天把这个股票卖出去了。然后各求一个最大值,最后你想要你的钱数非常多,你怎么办?肯定是要把手上持的所有股都卖出去,也就是说你手上最后达到你手上未持股的状态。

AC代码:

#include<iostream>
using namespace std;
const int N=1e5+5;
int n,dp[N][2],a[N];//前i天状态为j的最大钱数 
int main(){
	cin>>n;
	for (int i=1;i<=n;i++){
		cin>>a[i]; 
	}
	dp[0][1]=-0x3f3f3f;  //初始化(赋极小值) 
	for (int i=1;i<=n;i++){
		dp[i][1]=max(dp[i-1][0]-a[i],dp[i-1][1]);
		dp[i][0]=max(dp[i-1][0],dp[i-1][1]+a[i]);
	}
	cout<<dp[n][0];
	return 0;
} 

3.股票买卖III

时间限制:1秒        内存限制:128M

题目描述
给定一个长度为NN的数组,数组中的第 i 个数字表示一个给定股票在第i天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成两笔交易。

注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
输入格式
第一行包含整数NN,表示数组长度。

第二行包含N个不大于10^9​​的正整数,表示完整的数组。

输出格式
输出一个整数,表示最大利润。

数据范围
1≤N≤10​5​​

样例输入

83 3 5 0 0 3 1 4

样例输出

6

思路解析:

像遇到这种234的这种题的时候,一定要看两件东西。一键就是像什么轮数那种,他给你从固定的量。变数或者是给你变成了一个变的量,要么就是数据量增加了。读题后可知他就是把原来你能买无限次,变成了你只能买卖两次。遇到这种情况的变化应该怎么办?一般来说就是去更改重新设计DP数组的含义。那下面的c代码它是怎么改的?非常简单,就是原先状态有两种,一种就是你第i天持股,一种就是你第i天没有持股。现在就是你只能进行两次的交易,那么它会出现多少种情况呢?它会出现五种情况。分别是你不买不卖,1分钱不赚。第二种就是你买了一次,第三种就是你卖了一次。第四种是你买了两次,第五种是你卖了两次。Ac代码中的dp数组用零表示不进行交易,用1表示买一次。用2表示买两次。用3表示卖一次,用4表示卖2次。接下来非常重要的一点就是初始化在第0天,不管什么状态,他都要付一个极小值,防止在遍历的时候出现对最大值求值的时候,有负数,把这个零给他存进去。接下来就是遍历求值与第一题十分相似。最后的最后有一个非常重要的点,同时在输出的时候求最大值时一定要注意,就是他有两次可能卖出的情况,就是卖出一次,卖出两次,以及有时候他如果都是负数。你实际上不交易才是最好的,所以说一定要把不买卖的这个情况也加进去。

AC代码:

#include<iostream>
using namespace std;
const int N=1e5+5;
int n,dp[N][5],a[N];  
//0:不交易,1:买一次,2:买两次
//			3:卖一次,4:卖两次  
int main(){
	cin>>n;
	for (int i=1;i<=n;i++){
		cin>>a[i];
	}
	//初始化 
	dp[0][1]=-0x3f3f3f;
	dp[0][2]=-0x3f3f3f;
	dp[0][3]=-0x3f3f3f;
	dp[0][4]=-0x3f3f3f;
	for (int i=1;i<=n;i++){
		dp[i][0]=dp[i-1][0];
		dp[i][1]=max(dp[i-1][1],dp[i-1][0]-a[i]);
		dp[i][2]=max(dp[i-1][2],dp[i-1][3]-a[i]);
		dp[i][3]=max(dp[i-1][3],dp[i-1][1]+a[i]);
		dp[i][4]=max(dp[i-1][4],dp[i-1][2]+a[i]);
	}
	cout<<max(dp[n][0],max(dp[n][3],dp[n][4]));
	return 0;
} 

4.股票买卖IV

时间限制:1秒        内存限制:256M

题目描述
给定一个长度为N的数组,数组中的第 i 个数字表示一个给定股票在第ii天的价格。

设计一个算法来计算你所能获取的最大利润,你最多可以完成k笔交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。一次买入卖出合为一笔交易。
输入描述
第一行包含整数N和k, 表示数组的长度以及你可以完成的最大交易数量。

第二行包含N个不超过 10000 的正整数,表示完整的数组。
输出描述
输出一个整数,表示最大利润。

数据范围
1≤N≤10​5​​
1≤k≤100
样例输入

3 22 4 1

样例输出

2

思路解析:

还是根据上一题的讲解来看,先读题。我说的对啊,因为他更改的就是这多少笔交易,变成了一个变量,就是k笔交易。那这个怎么办到呢?以前状态都是已知的,现在状态是未知的。如果说一上来就直接做这个题,可能会很懵,但是前面做了那么多题作为铺垫。从上一个题就能找出一些规律来。就是当这个交易次数为偶数时就是一定为持股。他就出现了那么一个规律。你为50这个的最大值就是什么呢?就是你上一天同样的状态和你上以前不同的状态。然后卖出。不同的状态就是你持股的状态,然后卖出。而当这个交易次数为奇数时还是同样的等,要么就是你前一天已经有这个状态,要么就是你前一天持股状态。然后买入。但是这个交易次数不是指买卖。而是指110卖为一次,所以说在进行模拟的时候一定要二倍数组,也不要开太小。我的ac代码就出现了可能会报运行错误或可能会报答案错误的情况。为什么?就是因为第一批数组开小了,k虽然值最大值是100开105不会爆,但是他这一次交易等于一次买一次卖一次(二倍),所以说dp数组要开成205次的状态。最后遍历所有的交易次数,求一个最大值,然后要注意。不交易也是一种情况,也有可能是一种最大值。

AC代码:

#include<iostream>
using namespace std;
const int N=1e5+5;
int n,dp[N][105],a[N],k;  
int main(){
	cin>>n>>k;
	for (int i=1;i<=n;i++){
		cin>>a[i];
	}
	//初始化 
	for (int i=1;i<=2*k;i++){
		dp[0][i]=-0x3f3f3f;
	}
	for (int i=1;i<=n;i++){
		dp[i][0]=dp[i-1][0];
		for (int j=1;j<=2*k;j++){
			if (j%2==0){
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+a[i]);
			}
			else{
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]-a[i]); 
			}
		}
	}
	int maxx=0;
	for (int i=1;i<=2*k;i++){
		if (i%2==0){
			maxx=max(maxx,dp[n][i]); 
		}
	}
	maxx=max(maxx,dp[n][0]);
	cout<<maxx;
	return 0;
} 

5.CSP-J2019第二轮认证T3-纪念品

时间限制:1秒        内存限制:128M

题目描述

小伟突然获得一种超能力,他知道未来 T 天 N 种纪念品每天的价格。某个纪念品的价格是指购买一个该纪念品所需的金币数量,以及卖出一个该纪念品换回的金币数量。
每天,小伟可以进行以下两种交易无限次:
任选一个纪念品,若手上有足够金币,以当日价格购买该纪念品;
卖出持有的任意一个纪念品,以当日价格换回金币。
每天卖出纪念品换回的金币可以立即用于购买纪念品,当日购买的纪念品也可以当日卖出换回金币。当然,一直持有纪念品也是可以的。
T 天之后,小伟的超能力消失。因此他一定会在第 T 天卖出所有纪念品换回金币。
小伟现在有 M 枚金币,他想要在超能力消失后拥有尽可能多的金币。

输入描述

第一行包含三个正整数 T, N, M,相邻两数之间以一个空格分开,分别代表未来天数 T,纪念品数量 N,小伟现在拥有的金币数量 M。
接下来 T 行,每行包含 N 个正整数,相邻两数之间以一个空格分隔。第 i 行的 N 个正整数分别为 Pi,1,Pi,2,……,Pi,N,其中 Pi,j表示第 i 天第 j 种纪念品的价格。

输出描述

输出仅一行,包含一个正整数,表示小伟在超能力消失后最多能拥有的金币数量。

样例

输入

6 1 100
50
20
25
20
25
50

输出

305

提示

【输入输出样例 1 说明】
最佳策略是:
第二天花光所有 100 枚金币买入 5 个纪念品 1;
第三天卖出 5 个纪念品 1,获得金币 125 枚;
第四天买入 6 个纪念品 1,剩余 5 枚金币;
第六天必须卖出所有纪念品换回 300 枚金币,第四天剩余 5 枚金币,共 305 枚金币。
超能力消失后,小伟最多拥有 305 枚金币。
【输入输出样例 2 说明】
最佳策略是:
第一天花光所有金币买入 10 个纪念品 1;
第二天卖出全部纪念品 1 得到 150 枚金币并买入 8 个纪念品 2 和 1 个纪念品 3,剩余 1 枚金币;
第三天必须卖出所有纪念品换回216 枚金币,第二天剩余1枚金币,共 217 枚金币。
超能力消失后,小伟最多拥有 217 枚金币。

【数据规模与约定】
对于 10% 的数据,T = 1。
对于 30% 的数据,T≤4,N≤4,M≤100,所有价格10≤Pi,j≤100。
另有 15% 的数据,T≤100,N=1。
另有 15% 的数据,T=2,N≤100。
对于 100% 的数据,T≤100,N≤100,M≤10^3,所有价格 1≤P i,j≤10^4,数据保证任意时刻,小明手上的金币数不可能超过 10^4。

思路解析:

使其在落骨上也有相同的题。也有好多的题解。我承认我可能敢不上大神写的好,但是我认为我的思路也可以贡献出来。这个思路非常简单。首先讲一件事情,读完题以后要套模板。第一批的模板实际上就只有三套。他们的三套的基础都是01背包问题。那这三套分别是01背包,完全背包和多重背包。当然有些第一批题也有可能是其他的,但是相对于这种题来说它就是一个完全背包。接下来讲最重要的第一批数组是怎么设计的?Tt数组我是这么设计的,就是某一天。钱数为m的时候的最大收益。因为此题它的数据量比较大,所以、DP数组开成一维进行优化。正常的输入输出,但是因为它每次都会用,每一天都会用,所以一定要记得初始化。否则那些脏数据会影响最终的答案。每次都会去便利。无所有的物品。然后当这个他们之间有差值。也就是所谓的赚钱你才去买,因为此题他不可能再有负数的钱的情况下进行交易。接着会去从这个能够赚钱的这个位置买,一直到m,其中的m就相当于完全背包里的背包容量。然后你去求取一个最大值,要么不拿,要么拿。然后你能取不取到的这个最大值,也就是说你能赚到的最大钱数,每次都给它添加到已有的金币数里去更新不断更新。背包容量,最后输出这个所谓的背包容量也就是能赚到的总钱数。

AC代码:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int t,n,m;
int w[105][105];//第i天第j件物品价格
int dp[10005];  //某一天容量(钱)为i的时候的最大收益 
int main(){
	cin>>t>>n>>m;
	for (int i=1;i<=t;i++){
		for (int j=1;j<=n;j++){
			cin>>w[i][j];
		}
	}
	for (int i=1;i<t;i++){   //最后一天不会再买了
		memset(dp,0,sizeof(dp));   //每天初始化 
		for (int j=1;j<=n;j++){  
			if (w[i+1][j]>w[i][j]){  //赚钱才买 
				for (int k=w[i][j];k<=m;k++){  //遍历背包容量 
					dp[k]=max(dp[k],dp[k-w[i][j]]+w[i+1][j]-w[i][j]) ;
				}
			}
		} 
		m+=dp[m];  //更新钱数 
	}
	cout<<m; 
	return 0;
} 

  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值