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:
- 状态:定义fi表示以Ai为结尾的最长上升序列的长度,初始化f1=1
- 状态转换方程: 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+1∣j<i且aj<ai}
- 答案即为max(fi)
- 复杂度为O(n^2)
LCS :
- 状态: f [ i ] [ j ] f[i][j] f[i][j]为以ai,bj结尾的LCS长度
- 状态转换方程:当ai==bj时,
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
−
1
]
+
1
f[i][j] =f[i-1][j-1]+1
f[i][j]=f[i−1][j−1]+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[i−1][j],f[i][j−1]} - 最后答案即为f[n][m]
- 复杂度为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[i−1],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;
}