A - 签到题
题目:
东东在玩游戏“Game23”。
在一开始他有一个数字n,他的目标是把它转换成m,在每一步操作中,他可以将n乘以2或乘以3,他可以进行任意次操作。输出将n转换成m的操作次数,如果转换不了输出-1。
输入:
输入的唯一一行包括两个整数n和m(1<=n<=m<=5*10^8).
输出:
输出从n转换到m的操作次数,否则输出-1.
思想:
不断循环,能除以2除尽答案数+1,能除以3除尽答案数+1,并更新该数字,不能时就退出循环,最后如果剩下的为1,输出答案数,否则表明其不能除尽,输出-1。
代码:
#include <iostream>
using namespace std;
int main()
{
int n,m;
cin>>n>>m;
int change;
if(m%n!=0)
{
cout<<-1<<endl;
return 0;
}
else
{
change=m/n;
}
int th=0;
while(change!=1)
{
if(change%2==0)
{
change=change/2;
th++;
}
else if(change%3==0)
{
change=change/3;
th++;
}
else
{
break;
}
}
if(change==1) cout<<th<<endl;
else cout<<-1<<endl;
}
B - LIS & LCS
(注:LIS:longest increasing subsequence)
(注:LCS:longest common subsequence)
题目:
东东有两个序列A和B。
他想要知道序列A的LIS和序列AB的LCS的长度。
注意,LIS为严格递增的,即a1<a2<…<ak(ai<=1,000,000,000)。
输入:
第一行两个数n,m(1<=n<=5,000,1<=m<=5,000)
第二行n个数,表示序列A
第三行m个数,表示序列B
输出:
输出一行数据ans1和ans2,分别代表序列A的LIS和序列AB的LCS的长度
思想:
首先这两个都要用到动态规划的知识。
求LIS时,第一位先设为1,接下来,向后遍历,对于每一个新的位,遍历其前的所有已更新好的答案,找出当前该位可以作为前者延长位的长度最大的,然后当前位=该位长度+1.最后所有位中最大者就是答案。(时间复杂度O(n^2))
求LCS时,先定义一个答案矩阵,r[ i ][ j ]表示第一个数组中一直到 i ,第二个数组一直到 j 的LCS长度,起初,全部设为0,接下来二层遍历第一数组和第二数组,如果 i 和 j 位置上的数相等,表明两个数组分别加上当前位时LCS长度就会加 1,因此使得 r[ i ] [ j ]=r[i-1][j-1]+1。如果这两个位置上的数不相同,那么表明新加入的位并不能构成LCS的新位,因此r[ i ][ j ] =max(r[ i-1 ][ j ],r[ i ][ j-1 ])。最终r[n][m]便是答案。(n,m分别是两个数组的长度)(时间复杂度:O(n*m))
代码:
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
int a[5005],b[5005],f[5005];
int r[5005][5005];
int n,m;
int lcs()
{
//r[1][0]=0;r[0][1]=0;r[0][0]=0;
//memset(r,0,sizeof(r));
for(int i=0;i<=n;i++) r[i][0]=0;
for(int j=0;j<=m;j++) r[0][j]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(a[i]==b[j])
{
r[i][j]=r[i-1][j-1]+1;
}
else
{
r[i][j]=max(r[i-1][j],r[i][j-1]);
}
}
}
//cout<<r[n][m]<<endl;
return r[n][m];
}
int lis()
{
f[1]=1;
int result=1;
int max_zen;
for(int i=2;i<=n;i++)
{
max_zen=0;
for(int j=1;j<i;j++)
{
if(a[j]<a[i] && max_zen<f[j])
{
max_zen=f[j];
}
}
f[i]=max_zen+1;
if(result<f[i]) result=f[i];
}
//cout<<result<<endl;
return result;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%d",&b[i]);
}
int ans1=lis();
int ans2=lcs();
cout<<ans1<<" "<<ans2<<endl;
}
C - 拿数问题 II
题目:
YJQ 上完第10周的程序设计思维与实践后,想到一个绝妙的主意,他对拿数问题做了一点小修改,使得这道题变成了 拿数问题 II。
给一个序列,里边有 n 个数,每一步能拿走一个数,比如拿第 i 个数, Ai = x,得到相应的分数 x,但拿掉这个 Ai 后,x+1 和 x-1 (如果有 Aj = x+1 或 Aj = x-1 存在) 就会变得不可拿(但是有 Aj = x 的话可以继续拿这个 x)。求最大分数。
本题和课上讲的有些许不一样,但是核心是一样,需要你自己思考。
输入:
第一行包含一个整数 n (1 ≤ n ≤ 105),表示数字里的元素的个数
第二行包含n个整数a1, a2, ..., an (1 ≤ ai ≤ 105)
输出:
输出一个整数:n你能得到最大分值。
提示:
对于第三个样例:先选任何一个值为2的元素,最后数组内剩下4个2。然后4次选择2,最终得到10分。
思想:
一开始理解错了题意,以为取了一个后其左右两位置上的数无法取,后来才发现是加减1得到的数不再能取了。因此,考虑到这种情况,先将输入好的数组从小到大进行排序,并且将相邻的相等的数累加起来作为一位,这样就保证了所有的相同的数归为一位。并且用数组zhi存储每一位的原数(例:3 3 3 4 4,那么归一为9 8,数组zhi中为3 4)。接下来,遍历数组zhi,如果发现当前位 i 上 zhi[ i ]=zhi[ i-1]+1,那么要么当前数和前位数不可同时兼得,dp[i]=std::max(dp[i-1],dp[i-2]+a[i])。否则,dp[i]=std::max(dp[i-1],dp[i-2])+a[i],这样就可以得到结果。
代码:
#include <iostream>
#include <algorithm>
using namespace std;
long long a[100005];
long long zhi[100005];
int main()
{
int n;cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
sort(a,a+n);
int th=0;
long long count;
int bianli=0;
long long now;
while(bianli<n)
{
now=a[bianli];
count=0;
while(bianli<n && a[bianli]==now)
{
bianli++;
count++;
}
a[th]=count*now;
zhi[th]=now;
th++;
}
/*
for(int i=0;i<th;i++)
{
cout<<a[i]<<" "<<zhi[i]<<endl;
}
*/
long long dp[th];
dp[0]=a[0];
if(zhi[1]==zhi[0]+1)
dp[1]=std::max(a[0],a[1]);
else
dp[1]=a[0]+a[1];
for(int i=2;i<th;i++)
{
if(zhi[i]==zhi[i-1]+1)
dp[i]=std::max(dp[i-1],dp[i-2]+a[i]);
else
dp[i]=std::max(dp[i-1],dp[i-2])+a[i];
}
cout<<dp[th-1]<<endl;
}