动态规划1:Week10

1.

题目大意

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

思路

这个题直接按照题目要求,DFS搜索就可以,因为2,3互质,所以每一个数字转换的步数唯一的,具体实现的时候可以引入记忆化,到每一个数需要多少步可以记一下,在遇到的时候直接用。

代码
#include<bits/stdc++.h>
using namespace std;
typedef long long llong;
llong n,m;
map<llong,llong> cnt;//记忆化 
int solve(int i,int num)
{
	if(i==m) return 1;
	else if(i>m) return 0; 
	else {
		if(cnt.find(i*2)==cnt.end()){
			if(solve(i*2,num+1)) cnt[i*2] = num+1;
			}
	    if(cnt.find(i*3)==cnt.end())
	       if(solve(i*3,num+1)) cnt[i*3] = num+1; 
	}
}
int main()
{
	scanf("%lld%lld",&n,&m);
	cnt[n]=0;
    solve(n,0);
    if(cnt.find(m)==cnt.end())
	   printf("%d",-1);
	else
	   printf("%lld",cnt[m]);
	return 0;
} 

2 LIS&LCS

题目大意

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

思路

LIS:

  1. 状态:定义fi表示以Ai为结尾的最长上升序列的长度,初始化f1=1
  2. 状态转换方程: f i = m a x { f j + 1 ∣ j < i 且 a j < a i } f_i=max\{f_j+1|j<i且a_j<a_i \} fi=max{fj+1j<iaj<ai}
  3. 答案即为max(fi)
  4. 复杂度为O(n^2)

LCS :

  1. 状态: f [ i ] [ j ] f[i][j] f[i][j]为以ai,bj结尾的LCS长度
  2. 状态转换方程:当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
    当ai!=bj 时, 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]}
  3. 最后答案即为f[n][m]
  4. 复杂度为O(N*M)
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long llong;
int m,n;
llong a[10000],b[10000];
int ans1=0,ans2=0;
int f[10000];
int g[6000][6000];
void lis(){
	memset(f,0,sizeof f);
	int ma=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<i;j++){
			if(a[j]<a[i]) ma = max(ma,f[j]); // max value of the f[j]
		}
		f[i] = ma+1;
		ma=0; 
	}
	for(int i=1;i<=n;i++){
		ans1 = max(f[i],ans1);
	}
}
void lcs(){
	memset(g,0,sizeof g);
	g[0][0]=g[0][1]=g[1][0]=0;
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	 {
	 	if(a[i]==b[j]) g[i][j]=g[i-1][j-1]+1;
	 	else g[i][j] = max(g[i-1][j], g[i][j-1]);
	 }
	
}
int main()
{
//	freopen("in.txt","r",stdin);
	scanf("%d%d",&n,&m);
	memset(a,0,sizeof a);
	memset(b,0,sizeof b);
	for(int i=1;i<=n;i++) scanf("%lld",a+i);
	for(int j=1;j<=m;j++) scanf("%lld",b+j);
	lis();
	printf("%d ",ans1);
	lcs();
	printf("%d",g[n][m]);
	return 0;
} 

3.拿数问题

题目大意

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

思路

状态:f[i] 是拿不超过分数为i的对应的项,所获得的分值
状态转移:
如果i-1存在的话:
d p [ i ] = m a x { d p [ i − 1 ] , d p [ 序 列 中 小 于 i 的 数 中 , 第 二 大 的 ] + i } dp[i] = max\{dp[i-1],dp[序列中小于i的数中,第二大的]+i\} dp[i]=max{dp[i1]dp[i]+i}
如果i-1不存在的话,直接加
d p [ i ] = d p [ 序 列 中 小 于 i 的 数 中 , 第 一 大 的 ] + i dp[i]=dp[序列中小于i的数中,第一大的]+i dp[i]=dp[i]+i
答案:max{dp[i]}
复杂度:O(N)

代码

需要先计算一下,序列中每个数带来的分数,并按数字(不是分数)的大小,由小到大排序

#include<bits/stdc++.h>
using namespace std;
map<long long,long long>mp;
map<long long,long long>dp;
int n,a;
long long ans=0;
int main()
{
//	freopen("in.txt","r",stdin);
	scanf("%d",&n);
	mp.clear();
	dp.clear();
	dp[0] = 0;
	for(int i=0;i<n;i++){
		scanf("%d",&a);
		if(mp.find(a)!=mp.end()) mp[a]+=a;
		else mp[a]=a;
		dp[a] = 0; //init
	}	
	int j=0,k=0;
	for(auto i = mp.begin();i!=mp.end();i++)
	{
	    if(mp.find((i->first)-1)!=mp.end()) {
	    	dp[i->first] =  max(dp[(i->first)-1],dp[k]+i->second);
	    }
	    else dp[i->first]=i->second+dp[j];
//	    cout<<i->first<<":"<<dp[i->first]<<endl;
	    if(j!=0) k=j; // 可能的i->first -2 的取值 
	    j = i->first; // 可能的i->first -1 的取值 
	}
    for(auto j=dp.begin();j!=dp.end();j++){
    	ans = max(ans,j->second);
    }
	printf("%lld",ans);
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值