第五次oj动态规划

本文探讨了如何通过动态规划解决区间最大子段和问题,包括单数替换和矩阵最大子矩阵和。同时介绍了最长上升子序列、马拦过河卒问题的解法,以及如何在有限时间内计算出搬砖的最优策略。最后,讲解了如何找到两个字符串的最长公共子序列,展示了一系列与动态规划和最优化策略相关的算法应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

区间和

Description

金金最喜欢做有挑战的事情了,比如说求区间最大子段和。
一开始,金金有n个数,第i个数字是ai。
金金又找来了一个新的数字P,并想将这n个数字中恰好一个数字替换成P。要求替换后的最大子段和尽可能大。
金金知道这个题目仍然很简单,所以如果你做不出来,金金就要和你谈一谈了。
注:最大子段和是指在n个数中选择一段区间[L,R](L<=R)使得这段区间对应的数字之和最大。

Input

第一行两个数n,P。

接下来一行n个数ai。

Output

一个数表示答案。

Sample Input

5 3
-1 1 -10 1 -1

Sample Output

5

More Info

样例说明:将第三个数变成3后最大子段和为[2,4]。

数据范围:n<=1000,-1000<=ai,P<=1000。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int dp[1001][2]={0},i,j,n,x,a[10001],ans=-999999999,p;
	cin>>n>>p;
	for(i=1;i<=n;i++)
	{
		cin>>x;
		dp[i][0]=max(dp[i-1][0]+x,x);//为下一步做铺垫
		dp[i][1]=max(p,max(dp[i-1][0]+p,dp[i-1][1]+x));
		ans=max(ans,dp[i][1]);
	}
	cout<<ans<<endl;
	return 0;
}

[蓝桥杯][历届试题]最大子阵

Description

给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。

其中,A的子矩阵指在A中行和列均连续的一块。

Input

输入的第一行包含两个整数n, m,分别表示矩阵A的行数和列数。(1 ≤ n, m ≤ 500)

接下来n行,每行m个整数,表示矩阵A。

Output

输出一行,包含一个整数,表示A中最大的子矩阵中的元素和。

Sample Input

3 3
-1 -4 3
3 4 -1
-5 -2 8

Sample Output

10

More Info

WA?你考虑矩阵内全为负的情况了么

#include<bits/stdc++.h>
using namespace std;
int main()
{
	long long i,k,j,n,m,dp[501][501]={0},t,ans=INT_MIN,sum;
	cin>>n>>m;
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			cin>>t;
			dp[i][j]=dp[i-1][j]+t;//前i行的j列的和 
		}
	}
	for(i=1;i<=n;i++)//二维转换为一维 
	{
		for(j=1;j<=i;j++)//1到1->1到n行 
		{
			sum=0;
			for(k=1;k<=m;k++)
			{
				sum+=dp[i][k]-dp[j-1][k];//前i行的k列-前j-1行的k列 ,减一是因为最少得有一行
				ans=max(ans,sum);
				if(sum<0) sum=0;//舍弃k列的前边
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

最长上升子序列

Description

给定一长度为n的数列,数列中的每个数都在1~100000之间

请在不改变原数列顺序的前提下,从中取出一定数量的整数(不需要连续),并使这些整数构成单调上升序列。

输出这类单调上升序列的最大长度。

Input

输入包括两行,第一行为n,代表数列的长度。(1 ≤ n ≤ 100000)

第二行为n个整数。

Output

输出这类单调上升序列的最大长度

Sample Input

5
3 1 5 4 6

Sample Output

3

More Info

对于样例的解释:356,156,146 均可组成长度为3的上升子序列

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,a[100001],tot=0,s[100001],i,num;
	cin>>n;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	s[++tot]=a[1];
	for(i=1;i<=n;i++)
	{
		if(s[tot]<a[i])
			s[++tot]=a[i];
		else
		{
			num=lower_bound(s+1,s+tot+1,a[i])-s; //lower_bound二分查找 
			s[num]=a[i];
		}
	}
	printf("%d\n",tot);
	return 0;
}

过河卒

Description

大三老学长ZHB平时喜欢边晒晒太阳边下象棋养养老,有一天他突然想到了这么一个问题:

棋盘上AA点有一个过河卒,需要走到目标BB点。卒行走的规则:可以向下、或者向右。同时在棋盘上CC点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。

棋盘用坐标表示,AA点(0, 0)(0,0)、BB点(n, m)(n,m)(nn, mm为不超过2020的整数),同样马的位置坐标是需要给出的(假设马的位置不会在距离棋盘边缘两格范围内)。

现在ZHB学长要求你计算出卒从AA点能够到达BB点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。

Input

一行四个数据,分别表示BB点坐标和马的坐标。

Output

一个数据,表示所有的路径条数。

Sample Input

6 6 3 3

Sample Output

6

More Info

结果可能很大!

#include<bits/stdc++.h>
using namespace std;
long long mx,my,ux,uy,dp[101][101],book[101][101],d[8][2]={2,1,2,-1,1,2,1,-2,-1,2,-1,-2,-2,1,-2,-1};
int main()
{
	int i,j;
	cin>>ux>>uy>>mx>>my;
	book[mx+1][my+1]=1;
	for(i=0;i<8;i++)
		if(mx+d[i][0]>=0&&mx+d[i][0]<=ux&&my+d[i][1]>=0&&my+d[i][1]<=uy)
		book[mx+d[i][0]+1][my+d[i][1]+1]=1;
	dp[1][1]=1;//防止数组下标为负,集体加一右移
	for(i=1;i<=ux+1;i++)
		for(j=1;j<=uy+1;j++)
		{
			if(i==1&&j==1);
			else
			{
				if(book[i-1][j]==0)
					dp[i][j]+=dp[i-1][j];
				if(book[i][j-1]==0)
					dp[i][j]+=dp[i][j-1];
			}
		}
	cout<<dp[ux+1][uy+1]<<endl;
	return 0;
}

搬砖

Description

Sam暑假找了个搬砖的活儿,每天12个小时内有1~8种转可以搬,每种的时间的工资都不一样(如搬第一种砖的时间为0时~4时,工资为5元,搬第二种砖的时间为2时~6时,工资为8元),每次搬砖必须搬完才能搬下一次转(即不可重叠,如上例中搬第一种砖就不能搬第二种转),请你帮Sam拿拿主意,怎样安排可以赚到最多的工资?

Input

输入包含八组(八种砖),每组包含三个整数:搬砖开始时间s1、结束时间s2与工资sal。(0 <= s1 < 11 , 0 < s2 <= 11 , 1 <= sal <= 10)。

Output

Sam12小时内最多可以赚到的工资。

Sample Input

3 5 1
1 4 5
0 6 8
4 7 4
3 8 6
5 9 3
6 10 2
8 11 4

Sample Output

13

More Info

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int f[17][19]={0},x,y,g;
	for(int i=0;i<8;i++)
	{
		cin>>x>>y>>g;
		f[x][y]=g;
	}
	for(int i=1;i<=12;i++)//区间长度 
		for(int j=0;j<12-i;j++)//头 
		{
			int k=i+j;//尾 
			for(int t=j;t<k;t++)//过渡 
				f[j][k]=max(f[j][k],f[j][t]+f[t][k]);
		}
	cout<<f[0][11]<<endl;
	return 0;
 } 

最长公共子序列

Description

给定两个序列 X={x1,x2,…,xm} 和 Y={y1,y2,…,yn},找出X和Y的最长公共子序列。

Input

输入数据有多组,每组有两行 ,每行为一个长度不超过500的字符串(输入全是小写英文字母),表示序列X和Y。

Output

每组输出一行,表示所求得的最长公共子序列的长度,若不存在公共子序列,则输出0。

Sample Input

abcbdab
bdcaba

Sample Output

4

More Info

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
string a,b;
int dp[1001][1001];
int main()
{
	while(cin>>a>>b)
	{
		memset(dp,0,sizeof(dp));
		for(int i=1;i<=a.size();i++)
			for(int j=1;j<=b.size();j++)
			{
				if(a[i-1]==b[j-1])
					dp[i][j]=dp[i-1][j-1]+1;
				else
					dp[i][j]=max(dp[i-1][j],dp[i][j-1]); 
			}
		cout<<dp[a.size()][b.size()]<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值