dp专题(二)

avatar
作者
筋斗云
阅读量:0

洛谷:B3626 跳跃机器人

题目描述

地上有一排格子,共 nn 个位置。机器猫站在第一个格子上,需要取第 nn 个格子里的东西。

机器猫当然不愿意自己跑过去,所以机器猫从口袋里掏出了一个机器人!这个机器人的行动遵循下面的规则:

  • 初始时,机器人位于 11 号格子
  • 若机器人目前在 xx 格子,那么它可以跳跃到 x−1,x+1,2xx−1,x+1,2x 里的一个格子(不允许跳出界)

问机器人最少需要多少次跳跃,才能到达 nn 号格子。

输入格式

仅一行,一个正整数,表示 nn。

输出格式

仅一行,一个正整数,表示最少跳跃次数。

输入 #1复制

30

输出 #1复制

6

输入 #2复制

50

输出 #2复制

7

输入 #3复制

64

输出 #3复制

6

输入 #4复制

63

输出 #4复制

8 
样例解释

第一组样例:
1→2→4→8→16→15→301→2→4→8→16→15→30

第二组样例:
1→2→3→6→12→24→25→501→2→3→6→12→24→25→50

第三组样例:
1→2→4→8→16→32→641→2→4→8→16→32→64

第四组样例:
1→2→4→8→16→32→31→62→631→2→4→8→16→32→31→62→63

请注意在本组样例中,6363 不能通过 64−164−1 得到,因为格子总数为 6363,没有第 6464 个格子。

数据规模与约定

对于 100%100% 的数据,有 1≤n≤10000001≤n≤1000000。

做法

dp数组 下标:第i格 ; 值:步数

感觉有点递推关系的都可以考虑一下dp

#include<bits/stdc++.h> using namespace std; int n; int dp[1000010]; int main(){  	scanf("%d",&n); 	memset(dp,0x3f,sizeof(dp)); 	dp[1]=0;  	for(int i=2;i<=n;i++){  		dp[i]=dp[i-1]+1;  		if(i%2==0){ 			dp[i]=min(dp[i],dp[i/2]+1); 			dp[i-1]=min(dp[i-1],dp[i]+1); 		}  	}  	cout<<dp[n]; }

2024黑龙江省赛:J. Trade

题目

在一个繁荣的国家,金斯诺决定从事贸易。

这个国家由 𝑛∗𝑚n∗m 座城市组成。每个城市由一对整数 (𝑥,𝑦)(x,y) 表示,其中 1≤𝑥≤𝑛1≤x≤n 和 1≤𝑦≤𝑚1≤y≤m 表示其在网格中的位置。在城市 (𝑥,𝑦)(x,y) 中,物品的价格用 𝑎[𝑥][𝑦]a[x][y] 表示,到达该城市的旅行费用用 𝑏[𝑥][𝑦]b[x][y] 表示。

在踏上旅程之前,金斯诺需要你为他规划一条路线。路线必须符合以下条件:

  • 路线的起点必须是位于第一行第一列的城市,即 (1,1)(1,1) 。
  • 路线的终点必须是位于最后一行( 𝑥=𝑛x=n )或最后一列( 𝑦=𝑚y=m )的城市。
  • 金斯诺只能从城市 (𝑥𝑖,𝑦𝑖)(xi,yi) 移动到 (𝑥𝑖+1,𝑦𝑖)(xi+1,yi) 或 (𝑥𝑖,𝑦𝑖+1)(xi,yi+1) 。因此,对于路线中的每一步 𝑖i (路线中的最后一步除外), (𝑥𝑖+1,𝑦𝑖+1)(xi+1,yi+1) 必须选择在 (𝑥𝑖,𝑦𝑖)(xi,yi) 的正下方或正右方。

进入路线后,Kingsnow 将在路线的第一个城市 (1,1)(1,1) 购买一件物品。然后,他将任意选择路线上的另一个城市出售该物品。此外,从他开始旅行的城市到他出售物品的城市之间,每到一个城市,他都会支付旅行费用。

也就是说,对于任何给定的路线 (𝑥1,𝑦1),(𝑥2,𝑦2),...,(𝑥𝑘,𝑦𝑘)(x1,y1),(x2,y2),...,(xk,yk) ,Kingsnow 都会从区间 [2,𝑘][2,k] 中任意选择一个整数 𝑡t 。他在城市 (𝑥𝑡,𝑦𝑡)(xt,yt) 出售商品的利润计算公式为 Profit=𝑎[𝑥𝑡][𝑦𝑡]−𝑎[1][1]−∑𝑡𝑖=1𝑏[𝑥𝑖][𝑦𝑖]Profit=a[xt][yt]−a[1][1]−∑i=1tb[xi][yi]

金斯诺寻找的路线是,无论他选择在路线上的哪个城市出售物品,他都能获得非负利润。

输入

第一行包含两个整数 𝑛n 和 𝑚(1≤𝑛,𝑚≤1000)m(1≤n,m≤1000) - 行数和列数。确保 𝑛n 和 𝑚m 不同时等于 11 。

在接下来的 𝑛n 行中,每一行都包含范围为 [1,109][1,109] 的 𝑚m 个整数,表示物品在每个城市的价格。第 𝑥x 行中的第 𝑦y 个整数是 𝑎[𝑥][𝑦]a[x][y] 。

在接下来的 𝑛n 行中,每一行都包含 𝑚m 个整数,范围为 [1,109][1,109] ,表示每个城市的差旅费用。第 𝑥x 行中的第 𝑦y 个整数是 𝑏[𝑥][𝑦]b[x][y] 。

输出

如果至少有一条路由符合要求,则输出 "是",否则输出 "否"。

做法

dp数组 下标:位于i行j列  ; 值:旅行费用

#include<bits/stdc++.h> using namespace std;  int n,m; long long a[1010][1010],b[1010][1010],dp[1010][1010];  int main(){  	scanf("%d%d",&n,&m); 	memset(dp,0x3f3f3f3f3f3f3f3f,sizeof(dp));  	for(int i=1;i<=n;i++){ 		for(int j=1;j<=m;j++){ 			scanf("%lld",&a[i][j]); 		} 	} 	for(int i=1;i<=n;i++){ 		for(int j=1;j<=m;j++){ 			scanf("%lld",&b[i][j]); 		} 	} 	 	dp[1][0]=dp[0][1]=0; 	for(int i=1;i<=n;i++){ 		for(int j=1;j<=m;j++){ 			if(a[i][j]-a[1][1]-dp[i-1][j]>=0) dp[i][j]=min(dp[i-1][j]+b[i][j],dp[i][j]); 			if(a[i][j]-a[1][1]-dp[i][j-1]>=0) dp[i][j]=min(dp[i][j-1]+b[i][j],dp[i][j]); 		} 	} 	 	if(dp[n][m-1]!=0x3f3f3f3f3f3f3f3f||dp[n-1][m]!=0x3f3f3f3f3f3f3f3f) cout<<"YES"; 	else cout<<"NO"; }

牛客小白月赛95:E.相依

题目描述

此题为C题的hard版,只有aia_iai​的数据范围不同。

给你一个 nnn 个正整数组成的数组 a ,你每次操作可以选择一对 (i,j)( i, j )(i,j),满足 1≤i<j≤n1 \leq i < j \leq n1≤i<j≤n,且 ai=aja_{i} = a_{j}ai​=aj​ ,将 { ai,ai+1,...,aj−1,aja_{i} , a_{i+1} , ... , a_{j-1} , a_{j}ai​,ai+1​,...,aj−1​,aj​ } 这段数删除,操作之后数组变为 { a1,a2,...,ai−1,aj+1,...,an−1,ana_{1},a_{2}, ..., a_{i-1},a_{j+1}, ...,a_{n-1},a_{n}a1​,a2​,...,ai−1​,aj+1​,...,an−1​,an​ }。文文想知道最少要做多少次操作才能将数组变为空。

输入描述:

第一行一个正整数输入 nnn (1≤n≤5×105)( 1 \leq n \leq 5 \times 10^5 )(1≤n≤5×105)。

第二行依次输入 nnn 个正整数 a1,a2,...,an−1,ana_{1},a_{2},...,a_{n-1},a_{n}a1​,a2​,...,an−1​,an​,每个数之间以空格分开。(0≤ai≤n)( 0 \leq a_i \leq n )(0≤ai​≤n)

输出描述:

输出一行,代表最少的操作次数,如果无论如何都无法使数组为空就输出 -1。

示例1

输入

5 3 2 3 2 2

输出

2

说明

例如,n 为 5,数组为{3,2,3,2,2}\{3, 2, 3, 2, 2\}{3,2,3,2,2},第一次操作我们可以选择 i=1i = 1i=1,j=3j = 3j=3,把这一段删除后数组变为 {2,2}\{2, 2\}{2,2},第二次操作我们可以选择 i=1i = 1i=1,j=2j = 2j=2,把这一段删除后数组变为空,操作了两次。

示例2

输入

5 1 2 3 4 5

输出

-1

做法

dp数组  下标:消除前i个   ;  值:最小操作次数

这题一开始我在怀疑dp的正确性,然后举了几个样例发现他确实能完美解决,还挺神奇的。只能说以后尽量往这个方向靠了,太难想了。

不过最值是一个标志。

//O(n^2)做法 #include<bits/stdc++.h> using namespace std; int n; int a[500010],dp[500010]; int main(){      memset(dp,0x3f,sizeof(dp));     scanf("%d",&n);     for(int i=1;i<=n;i++){         scanf("%d",&a[i]);     }      dp[0]=0;      for(int i=1;i<=n;i++){          for(int j=1;j<i;j++){              if(a[i]==a[j]){//找前面和当前数字相同的且操作数最小的                 dp[i]=min(dp[i],1+dp[j-1]);             }          }      }      if(dp[n]==0x3f3f3f3f) cout<<-1;     else cout<<dp[n];  }   //O(n)做法 #include<bits/stdc++.h> using namespace std;  int n; int a[500010],dp[500010],minn[500010];  int main(){      memset(dp,0x3f,sizeof(dp));     memset(minn,0x3f,sizeof(dp));     scanf("%d",&n);     for(int i=1;i<=n;i++){         scanf("%d",&a[i]);     }      dp[0]=0;          for(int i=1;i<=n;i++){          dp[i]=minn[a[i]];//前面和当前数字相同的且操作数最小的可以用数组minn来维护,实现降低复杂度         minn[a[i]]=min(minn[a[i]],dp[i-1]+1);      }      if(dp[n]!=0x3f3f3f3f) cout<<dp[n];     else cout<<-1;   } 

2024河南省赛:L-Toxel 与 PCPC II

做法

dp数组  考虑了第i个bug ,解决了j个bug  ; 值:需要的时间 

有时候用滚动数组也会超时,直接降维就好了。

这题的关键在于:如果连续修复太多个bug的话这个数得四次方是远大于从头开始扫描的时间,22的四次方刚好是大于2e5最小整数,所以就可以利用这点降低复杂度。

//最开始的思路 #include<bits/stdc++.h> using namespace std;  int n,m; long long a[200010],dp[2010][2010];  int main(){ 	 	scanf("%d%d",&n,&m); 	for(int i=1;i<=m;i++) scanf("%lld",&a[i]); 	for(int i=0;i<=m;i++){ 		for(int j=0;j<=m;j++){ 			dp[i][j]=0x3f3f3f3f3f3f3f3f; 		} 	} 	 	sort(a+1,a+1+m); 	dp[0][0]=0; 	 	for(int i=1;i<=m;i++){ 		 		int j=0; 		if(i-j>22) j=i-21; 		 		for(;j<=i;j++){ 			 			if(j==0) dp[i][j]=0;//一个都不解决  			 			else if(i!=j) dp[i][j]=min(dp[i][j],dp[j][j]);//只解决了前j个不解决  			 			if(i==j){//全部都解决了 			  				int k=0; 				if(j-k>22) k=j-22; 				 				for(;k<j;k++){//i个问题解决了k个  				 					if(k==0) dp[i][j]=min(dp[i][j],a[i]+1ll*i*i*i*i);//一次性解决  					 					else dp[i][j]=min(dp[i][j],a[i]+1ll*(i-k)*(i-k)*(i-k)*(i-k)+dp[k][k]);//已经解决了前k个  					 				} 				 			} 			 		}  	} 	 	cout<<dp[m][m]; 	 }   //可以看到,数组开不下,用滚动数组也会超时。但我们可以发现,这题直接降维就好了,因为大的值由小的覆盖,不用担心上层和这层乱赋值的情况。 #include<bits/stdc++.h> using namespace std; int n,m; long long a[200010],dp[200010]; int main(){ 	 	scanf("%d%d",&n,&m); 	for(int i=1;i<=m;i++) scanf("%lld",&a[i]); 	for(int j=0;j<=m;j++){ 		dp[j]=0x3f3f3f3f3f3f3f3f; 	} 	 	sort(a+1,a+1+m); 	dp[0]=0; 	 	for(int i=1;i<=m;i++){ 		 		int j=0; 		if(i-j>22) j=i-21; 		 		for(;j<=i;j++){ 			 			 			 			if(j==0) dp[j]=0;//不解决  			 			else if(i!=j) dp[j]=min(dp[j],dp[j]); 			 			if(i==j){ 				 				int k=0; 				if(j-k>22) k=j-21; 				 				for(;k<j;k++){ 					 					if(k==0) dp[j]=min(dp[j],a[i]+1ll*i*i*i*i); 					 					else dp[j]=min(dp[j],a[i]+1ll*(i-k)*(i-k)*(i-k)*(i-k)+dp[k]); 					 				} 				 			} 	 		}  	} 	 	cout<<dp[m]; 	 }

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!