签到题
问题描述
东东在玩游戏“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[i−1][j−1]+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[i−1][j],f[i][j−1]),为其相邻两个状态的最大值。最终的答案为
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你能得到最大分值。
样例输入
1:
2
1 2
2:
3
1 2 3
3:
9
1 2 1 3 2 2 2 2 3
样例输出
1:
2
2:
4
3:
10
解题思路
对于这个题,关键是要处理一个数是可拿还是不可拿,由于刚开始输入的数是无序的,所以我们不好判断相邻两个数的关系。所以我们先对输入的数进行排序,然后将所有相同的数加到一起,然后进行遍历。对每一个值的和,我们首先判断它是否等于它左边的数加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;//输出数组最末的值,为最大值
}
}