动态规划习题

签到题

问题描述

东东在玩游戏“Game23”。
在一开始他有一个数字n,他的目标是把它转换成m,在每一步操作中,他可以将n乘以2或乘以3,他可以进行任意次操作。输出将n转换成m的操作次数,如果转换不了输出-1。

input:
输入的唯一一行包括两个整数n和m(1<=n<=m<=5*10^8).
output:
输出从n转换到m的操作次数,否则输出-1.

样例输入

1.  120 51840
2.  42 42
3.  48 72

样例输出

1. 7
2. 0
3. -1

解题思路

对于这个题,如果n能转换成m,那么m必是n的整数倍,所以我们可以通过一个求余操作来进行确定,如果不是,则输出-1。如果是,则就判断能否通过若干次操作得到它。由于进行若干次操作实质上就是将n放大了多少倍,则我们可以先计算出m是n的多少倍,然后在一次进行对3求余或对2求余操作,如果没有余数,则可以通过一步操作到达,操作次数加一,否则不能得到,输出-1。

代码

#include<iostream>
using namespace std;
int main()
{
	long long int n,m;
	cin>>n>>m;
	if(n==m)
		cout<<"0"<<endl;//相等则输出0
	else if(n>m)
		cout<<"-1"<<endl;//如果n比m大,不可能完成,输出-1		
	else
	{
		if(m%n!=0)//如果m不是n的整数倍,输出-1
			cout<<"-1"<<endl;
		else
		{
			long long int count=0;
			long long int temp=m/n;//计算倍数
			while(temp!=1)
			{
				if(temp%3==0)//将倍数对3求余
				{
					count++;
					temp/=3;
					continue;
				}
				else if(temp%2==0)//将倍数对2求余
				{
					count++;
					temp/=2;
					continue;
				}
				else
				{
					cout<<"-1"<<endl;//不能整除,得不到,输出-1
					return 0;
				}
			}
			if(count!=0)
				cout<<count<<endl;
		}
	}
	return 0;
} 

LIS&LCS

问题描述

东东有两个序列A和B。
他想要知道序列A的LIS和序列AB的LCS的长度。
注意,LIS为严格递增的,即a1<a2<…<ak(ai<=1,000,000,000)。

input:
第一行两个数n,m(1<=n<=5,000,1<=m<=5,000)
第二行n个数,表示序列A
第三行m个数,表示序列B
output:
输出一行数据ans1和ans2,分别代表序列A的LIS和序列AB的LCS的长度

解题思路:

若要在n个整数中求最长上升序列,我们可以定义一个数组 f i f_{i} fi来表示以 A i A_{i} Ai为结尾的最长上升序列,在求解 f i f_{i} fi时,则是要遍历其前面的元素,找到一个最大并且的 f j f_{j} fj并且 A j A_{j} Aj要小于 A i A_{i} Ai,则我们可以得出 f i = f j + 1 f_{i}=f_{j}+1 fi=fj+1。最后的答案为 f i f_{i} fi中的最大值。
对于求两个序列的最长公共子序列,和上一方法类似,我们可以通过一个二维数组 f [ i ] [ j ] f[i][j] f[i][j]来表示 A 1 , A 2 , . . . , A i A_{1},A_{2},...,A_{i} A1,A2,...,Ai B 1 , B 2 , . . . , B j B_{1},B_{2},...,B_{j} B1,B2,...,Bj的LCS长度,如果在进行遍历时,遇到 A i = = B j A_{i}==B_{j} Ai==Bj,则 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + 1 f[i][j]=f[i-1][j-1]+1 f[i][j]=f[i1][j1]+1,即其上一个相等状态加1,否则, f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i ] [ j − 1 ] ) f[i][j] = max(f[i-1][j], f[i][j-1]) f[i][j]=max(f[i1][j],f[i][j1]),为其相邻两个状态的最大值。最终的答案为 f [ n ] [ m ] f[n][m] f[n][m]

代码

#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
int a[5010],b[5010],f[5010]; 
int d[5010][5010];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=m;i++)
		cin>>b[i];
	f[1]=1;
	for(int i=2;i<=n;i++)
	{//求最长上升序列
		int mx=0;
		for(int j=1;j<i;j++)
		{
			if(a[j]<a[i])
			{//在其前面找到满足a[j]<a[i]并且长度最大的
				if(f[j]>mx)
					mx=f[j];
			}
		}
		f[i]=mx+1;
	}
	d[1][0]=d[0][0]=d[0][1]=0;//求解最长公共序列
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(a[i]==b[j])//若相等,则其长度加1
				d[i][j]=d[i-1][j-1]+1;
			else//否则,选取相邻状态最大的值
				d[i][j]=max(d[i-1][j],d[i][j-1]);
		}
	}
	int mxlis=0;
	for(int i=1;i<=n;i++)
	{
		if(f[i]>mxlis)
			mxlis=f[i];
	}
	cout<<mxlis<<" "<<d[n][m]<<endl;//输出
} 

拿数问题 II

问题描述:

给一个序列,里边有 n 个数,每一步能拿走一个数,比如拿第 i 个数, Ai = x,得到相应的分数 x,但拿掉这个 Ai 后,x+1 和 x-1 (如果有 Aj = x+1 或 Aj = x-1 存在) 就会变得不可拿(但是有 Aj = x 的话可以继续拿这个 x)。求最大分数。

input:
第一行包含一个整数 n (1 ≤ n ≤ 105),表示数字里的元素的个数
第二行包含n个整数a1, a2, …, an (1 ≤ ai ≤ 105)
output:
输出一个整数:n你能得到最大分值。

样例输入

12
1 2
23
1 2 3
39
1 2 1 3 2 2 2 2 3

样例输出

12
24
310

解题思路

对于这个题,关键是要处理一个数是可拿还是不可拿,由于刚开始输入的数是无序的,所以我们不好判断相邻两个数的关系。所以我们先对输入的数进行排序,然后将所有相同的数加到一起,然后进行遍历。对每一个值的和,我们首先判断它是否等于它左边的数加1,如果等于,就要判断这个数取还是不取,即比较其左边的数的和与其左边两个和其的和谁大, 从中取出最大的。如果不相等,则直接累加,最后输出最右边的数。

代码

#include<iostream>
#include<algorithm>
using namespace std;
struct point{
	long long int thesum;//值为value的所有数的和
	long long int value;
	point(){
		thesum=0;
		value=0;
	} 
};
point p[100010];
int n,all[100010];
int main()
{
	cin>>n;
	for(int i=0;i<n;i++) 
		cin>>all[i];
	sort(all,all+n);//排序
	int flag=0;
	p[0].thesum=p[0].value=all[0]; 
	for(int i=1;i<n;i++)
	{
		if(all[i]==all[i-1])
			p[flag].thesum+=all[i];//将相同的数加起来存到p数组中
		else
		{
			flag++;
			p[flag].thesum+=all[i];
			p[flag].value=all[i];
		}
	}
	if(flag==0)
		cout<<p[0].thesum<<endl;//所有数都相等,输出p[0]
	else
	{//判断p[1]和p[0]的关系,求出p[1],便于转移
		if(p[1].value==p[0].value+1)
			p[1].thesum=max(p[0].thesum,p[1].thesum);
		else
			p[1].thesum+=p[0].thesum;
		for(int i=2;i<=flag;i++)
		{
			if(p[i].value==p[i-1].value+1)//若其等于第一位的值加1
				p[i].thesum=max(p[i-1].thesum,p[i-2].thesum+p[i].thesum);//求低一位的和与低两位和其相加中的最大值
			else//否则累加
				p[i].thesum+=p[i-1].thesum;
		}
		cout<<p[flag].thesum<<endl;//输出数组最末的值,为最大值
	}
} 
### 关于PTA平台上动态规划习题的信息 对于希望在 PTA 平台上找到并解决动态规划习题的用户来说,虽然具体题目列表会随时间更新而变化,但可以提供一些指导来帮助定位这些资源。 #### 动态规划简介 动态规划是一种通过把原问题分解成更简单的子问题的方式来求解复杂问题的方法。这种方法主要用于优化问题,在计算机科学领域有着广泛的应用场景[^1]。 #### 如何在PTA上查找动态编程习题 为了有效利用 PTA 平台进行学习: - **分类浏览**:访问 PTA 官方网站后,可以通过课程或标签筛选功能直接进入算法类目下的动态规划专题。 - **关键词搜索**:使用站内搜索引擎输入 "dynamic programming" 或者中文 “动态规划”,能够快速获取到一系列相关的练习题集。 - **参与竞赛/作业**:很多在线评测系统都会定期举办针对特定主题的比赛或者布置家庭作业形式的任务,这也是接触高质量动态规划题目的好机会之一。 #### 示例代码片段展示经典背包问题解决方案 下面给出一段 Python 实现的经典 0-1 背包问题解答作为例子: ```python def knapsack(weights, values, capacity): n = len(values) dp = [[0]*(capacity+1) for _ in range(n+1)] for i in range(1, n+1): for w in range(capacity+1): if weights[i-1] <= w: dp[i][w] = max(dp[i-1][w], dp[i-1][w-weights[i-1]] + values[i-1]) else: dp[i][w] = dp[i-1][w] return dp[n][capacity] ``` 此函接收物品重量组 `weights`、价值组 `values` 和背包容量 `capacity` 参,并返回最可获得总价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值