A-签到题
题目描述
东东在玩游戏“Game23”。
在一开始他有一个数字n,他的目标是把它转换成m,在每一步操作中,他可以将n乘以2或乘以3,他可以进行任意次操作。输出将n转换成m的操作次数,如果转换不了输出-1。
Input
输入的唯一一行包括两个整数n和m(1<=n<=m<=5*10^8).
Output
输出从n转换到m的操作次数,否则输出-1.
样例
Simple Input 1
120 51840
Simple Output 1
7
Simple Input 2
42 42
Simple Output 2
0
Simple Input 3
48 72
Simple Output 3
-1
思路分析
首先m一定是n的倍数,得到m除以n的商赋给m,先除尽其中的6(即除尽了2或3),每除一次ans+2(一次2,一次3),若此时为奇数,3未除尽,再除尽3,ans++;否则为偶数,2未除尽,再除尽2,ans++。最后m应为1 。
AC代码
#include<iostream>
using namespace std;
//201820130210
int main()
{
int n,m;
int ans=0;
cin>>n>>m;
if(m%n==0)//m是n的倍数
{
m=m/n;
while(m>=6)
{
if(m%6==0)
{
m=m/6;
ans+=2;
}
else
break;
}
if(m%2==1)//m因子有奇数,除尽3
{
while(m>=3)
{
if(m%3==0)
{
m=m/3;
ans++;
}
else
break;
}
}
else
{
//再除尽2,最后m应该为1
while(m>=2)
{
if(m%2==0)
{
m=m/2;
ans++;
}
else
break;
}
}
if(m==1) cout<<ans;
else
cout<<"-1";
}
else
cout<<"-1";
return 0;
}
B - 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的长度。
样例输出
Simple Input
5 5
1 3 2 5 4
2 4 3 1 5
Simple Output
3 2
思路分析
LIS(最长上升子序列):n个整数Ai——An,从左到右选择尽量多的数,组成的一个升序序列。
LCS(最长公共子序列):两个序列A、B,从A、B中选出两个相同的子序列,使子序列尽可能长。
动态规划问题
使用课上学到的LIS、LCS的转移方程。数组LIS存放到A[i]的最长上升子序列的长度,LIS[1]=1,LIS[i]=max{LIS[i] | j<i&&Aj<Ai } +1,答案为max{LIS[i] }。
二维数组LCS存放到A[i],B[j]的最长公共子序列的长度。LCS[0][0]=LCS[0][1]=LCS[1][0],当A[i]==B[j]时,LCS[i][j]=LCS[i-1][j-1]+1;否则,LCS[i][j]=max(LCS[i][j-1],LCS[i-1][j])。答案为LCS[n][m]。
AC代码
#include<iostream>
#include <algorithm>
using namespace std;
int a[5010],b[5010],lis[5010],lcs[5010][5010];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=m;i++)
{
cin>>b[i];
}
//
lis[1]=1;
for(int i=2;i<=n;i++)
{
int Max=0;
for(int j=1;j<i;j++)
{
if(a[j]<a[i]&&lis[j]>Max) Max=lis[j];
}
lis[i]=Max+1;
}
lcs[0][0]=lcs[1][0]=lcs[0][1]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(a[i]==b[j]) lcs[i][j]=lcs[i-1][j-1]+1;
else
{
lcs[i][j]=max(lcs[i-1][j],lcs[i][j-1]);
}
}
}
sort(lis+1,lis+n+1);
cout<<lis[n]<<" "<<lcs[n][m];
return 0;
}
C - 拿数问题 II
题目描述
YJQ 上完第10周的程序设计思维与实践后,想到一个绝妙的主意,他对拿数问题做了一点小修改,使得这道题变成了 拿数问题 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你能得到最大分值。
样例
Input
2
1 2
Output
2
Input
3
1 2 3
Output
4
Input
9
1 2 1 3 2 2 2 2 3
Output
10
思路分析
拿数问题 1
n个数A[1]到A[n],若拿走数A[i],则数A[i-1],A[i+1]不可拿,状态转移方程dp[i]=max(dp[i-1],dp[i-2]+A[i])。
拿数问题2
与1不同的是,若拿走数A[i],则数A[i]-1,A[i]+1不可拿。数组ans存能拿到的最大分数,则从ans[min{A[i]}]开始到ans[max{A[i]}],ans[i]= max(ans[i-1],ans[i-2]+A[i]),因为每个数出现不止一次,所以用数组num存每个数组出现的次数,所以最后的状态转移方程为ans[i]= max(ans[i-1],ans[i-2]+A[i]*num[A[i]])。
注意数组类型要是long long型,因为最后答案是拿数的和,若有10^5个10 ^5,将 超过int。
AC代码
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
long long a[100001]={0};//元素
long long num[100001]={0};//次数
long long ans[100001]={0};//每一步的最大值
//如果有10^5个10 ^5,超过int
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
num[a[i]]++;
}
sort(a+1,a+n+1);
long long Max=0;
for(long long i=a[1];i<=a[n];i++)
{
ans[i]=max(ans[i-1],ans[i-2]+i*num[i]);
Max=max(Max,ans[i]);
}
cout<<Max;
return 0;
}