1、回文序列
题目描述
如果一个数字序列逆置之后跟原序列是一样的就称这样的数字序列为回文序列。例如:
{1, 2, 1}, {15, 78, 78, 15} , {112} 是回文序列,
{1, 2, 2}, {15, 78, 87, 51} ,{112, 2, 11} 不是回文序列。
现在给出一个数字序列,允许使用一种转换操作:
选择任意两个相邻的数,然后从序列移除这两个数,并用这两个数字的和插入到这两个数之前的位置(只插入一个和)。
现在对于所给序列要求出最少需要多少次操作可以将其变成回文序列。
输入描述:
输入为两行
第一行为序列长度n ( 1 ≤ n ≤ 50)
第二行为序列中的n个整数item[i] (1 ≤ iteam[i] ≤ 1000),以空格分隔。
输出描述:
输出一个数,表示最少需要的转换次数
示例1
输入
4
1 1 1 3输出
2
思路
一头一尾双指针跟踪,分别求累加和sum1和sum2:
sum1==sum2,sum1=sum2=0;
sum1 < sum2,头指针后移,sum1累加;
sum1 > sum2,尾指针前移动,sum2累加。
代码
#include<iostream>
#include<vector>
using namespace std;
int main(){
int n;
cin>>n;
cin.ignore();
vector<int> arr(n);
for(int i=0; i<n; ++i){
cin>>arr[i];
}
int i=0, j=n-1;
int suml=arr[0];
int sumr=arr[n-1];
int count=0;
while(i<j){
if(suml==sumr){
i++;
j--;
suml=arr[i];
sumr=arr[j];
}
else if(suml<sumr){
i++;
suml+=arr[i];
count++;
}
else if(suml>sumr){
j--;
sumr+=arr[j];
count++;
}
}
cout<<count<<endl;
}
2、优雅的点
题目描述
小易有一个圆心在坐标原点的圆,小易知道圆的半径的平方。小易认为在圆上的点而且横纵坐标都是整数的点是优雅的,小易现在想寻找一个算法计算出优雅的点的个数,请你来帮帮他。
例如:半径的平方如果为25
优雅的点就有:(+/-3, +/-4), (+/-4, +/-3), (0, +/-5) (+/-5, 0),一共12个点。
输入描述:
输入为一个整数,即为圆半径的平方,范围在32位int范围内。
输出描述:
输出为一个整数,即为优雅的点的个数
示例1
输入
25
输出
12
思路
圆的方程:x^2+y^2=r^2
利用圆的对称性,只求一个象限[ 0, pi )内优雅点的个数,结果乘以4。有以下方案:
- x和y两层循环,复杂度过大,超时,不可行;
- x取( 0, r ]内的整数,依次求相应的y,y为整数,则计数值count++,
代码
#include<iostream>
#include<math.h>
using namespace std;
int main(){
int n;
cin>>n;
double r=sqrt(n);
int count=0;
for(int i=1; i<=r; ++i){
double j=sqrt(n-i*i);
if(j==int(j))
count++;
}
count*=4;
cout<<count<<endl;
return 0;
}
3、跳石板
题目描述
小易来到了一条石板路前,每块石板上从1挨着编号为:1、2、3…….
这条石板路要根据特殊的规则才能前进:对于小易当前所在的编号为K的 石板,小易单次只能往前跳K的一个约数(不含1和K)步,即跳到K+X(X为K的一个非1和本身的约数)的位置。 小易当前处在编号为N的石板,他想跳到编号恰好为M的石板去,小易想知道最少需要跳跃几次可以到达。
例如:
N = 4,M = 24:
4->6->8->12->18->24
于是小易最少需要跳跃5次,就可以从4号石板跳到24号石板
输入描述:
输入为一行,有两个整数N,M,以空格隔开。
(4 ≤ N ≤ 100000)
(N ≤ M ≤ 100000)
输出描述:
输出小易最少需要跳跃的步数,如果不能到达输出-1
示例1
输入
4 24
输出
5
思路
用一个长度为m+1的一维数组dp,每个元素的初始值均为INT_MAX, 由于需要的是最小值,所以把初始值设置为INT_MAX方便后续的比较。
dp[i]表示跳到第i块石板的最少跳跃次数,dp[m]则表示跳到第m块石板所需要的最少跳跃次数,若最终dp[m]的值仍然为INT_MAX,则说明不能到达第m块石板,输出-1。
到达第i块石板以后:
- 求i的约数的集合divisor,集合divisor中的值就是下一次可以跳跃的步长;
- 下一次跳跃可以到达第i+divisor[j]块石板,所需要的最少跳跃次数为min(dp[i+divisor[j]], dp[i]+1)。
代码
#include<iostream>
#include<vector>
#include<climits>
#include<math.h>
using namespace std;
int div(vector<int> &divisor, int n){
int tmp=sqrt(n);
for(int i=2; i<=tmp; ++i){
if(n%i==0){
divisor.push_back(i);
}
if(i*i!=n && n%(n/i)==0){
divisor.push_back(n/i);
}
}
return 0;
}
int main(){
int n,m;
int count;
while(cin>>n>>m){
vector<int> dp(m+1,INT_MAX);
dp[n]=0;
for(int i=n; i<=m; ++i){
if(dp[i]==INT_MAX)
continue;
vector<int> divisor;
div(divisor,i);
for(int j=0; j<divisor.size(); ++j){
if(i+divisor[j]<=m){
dp[i+divisor[j]]=(dp[i+divisor[j]]<=dp[i]+1?dp[i+divisor[j]]:dp[i]+1);
}
}
}
if(dp[m]==INT_MAX){
cout<<-1<<endl;
}
else{
cout<<dp[m]<<endl;
}
}
return 0;
}
4、暗黑的字符串
题目描述
一个只包含’A’、’B’和’C’的字符串,如果存在某一段长度为3的连续子串中恰好’A’、’B’和’C’各有一个,那么这个字符串就是纯净的,否则这个字符串就是暗黑的。例如:
BAACAACCBAAA 连续子串”CBA”中包含了’A’,’B’,’C’各一个,所以是纯净的字符串
AABBCCAABB 不存在一个长度为3的连续子串包含’A’,’B’,’C’,所以是暗黑的字符串
你的任务就是计算出长度为n的字符串(只包含’A’、’B’和’C’),有多少个是暗黑的字符串。
输入描述:
输入一个整数n,表示字符串长度(1 ≤ n ≤ 30)
输出描述:
输出一个整数表示有多少个暗黑字符串
示例1
输入
2
3输出
9
21
思路
假设f(n)表示长度为n的暗黑字符串数目,表示最后两位不同的暗黑字符串数目,S(n)表示最后两位相同暗黑字符串数目,则f(n)=D(n)+S(n)。
往前推可以知道:
D(n)=D(n-1)+S(n-1)*2
D(n-1)即长度为n-1的暗黑字符串,最后两位是AB、BA、AC、CA、BC、CB,在此情况下,其第n位只能是与第n-2位相同的字符,即ABA、BAB、ACA、CAC、BCB、CBC;
S(n-1)即长度为n-1的暗黑字符串,最后两位是AA、BB、CC,在此情况下,其第n位可以是另外两个字符,即AAB、AAC、BBA、BBC、CCA、CCB。S(n)=D(n-1)+S(n-1)
D(n-1)即长度为n-1的暗黑字符串,最后两位是AB、BA、AC、CA、BC、CB,在此情况下,其第n位只能是与第n-1位相同的字符,即ABB、BAA、ACC、CAA、BCC、CBB;
S(n-1)即长度为n-1的暗黑字符串,最后两位是AA、BB、CC,在此情况下,其第n位只能与前面相同的字符,即AAA、BBB、CCC。
综上所述:
- 输入数字为1时,直接输出3;
- 用大小为 2*( n+1 ) 矩阵dp,dp[0][i]表示D(i),dp[1][i]表示S(i);
- 初始化时令dp[0][2]=6,dp[1][2]=3;
- dp[0][i] = dp[0][i-1] + 2*dp[1][i-1]
dp[1][i] = dp[0][i-1] + dp[1][i-1]
代码
#include<iostream>
#include<vector>
using namespace std;
int main() {
int n;
cin >> n;
long long res;
if (n == 1)
res = 3;
else {
vector<vector<long long> > dp(2, vector<long long>(n + 1));
dp[0][2] = 6;
dp[1][2] = 3;
for (int i = 3; i <= n; ++i) {
dp[0][i] = dp[0][i - 1] + dp[1][i - 1] * 2;
dp[1][i] = dp[0][i - 1] + dp[1][i - 1];
}
res = dp[0][n] + dp[1][n];
}
cout << res << endl;
return 0;
}
5、数字翻转
题目描述
对于一个整数X,定义操作rev(X)为将X按数位翻转过来,并且去除掉前导0。例如:
如果 X = 123,则rev(X) = 321;
如果 X = 100,则rev(X) = 1.
现在给出整数x和y,要求rev(rev(x) + rev(y))为多少?
输入描述:
输入为一行,x、y(1 ≤ x、y ≤ 1000),以空格隔开。
输出描述:
输出rev(rev(x) + rev(y))的值
示例1
输入
123 100
输出
223
思路
获得数字x的每位数字:
- 获取当前数x的个位:tmp=x%10;
- 丢弃x的个位:x/=10;
- 把取得的个位数放入数组r中:r.push_back(tmp);
这样得到的数组r已经是数字x的逆序,遍历r就可以求得最终结果。
代码
#include<iostream>
#include<vector>
using namespace std;
int rev(int x){
vector<int> r;
while(x){
int tmp=x%10;
r.push_back(tmp);
x/=10;
}
int res=0;
for(int i=0; i<r.size(); ++i){
res=10*res+r[i];
}
return res;
}
int main(){
int x,y;
cin>>x>>y;
int res;
res=rev(rev(x)+rev(y));
cout<<res<<endl;
return 0;
}
6、最大的奇约数
题目描述
小易是一个数论爱好者,并且对于一个数的奇数约数十分感兴趣。一天小易遇到这样一个问题: 定义函数f(x)为x最大的奇数约数,x为正整数。 例如:f(44) = 11.
现在给出一个N,需要求出 f(1) + f(2) + f(3)…….f(N)
例如: N = 7
f(1) + f(2) + f(3) + f(4) + f(5) + f(6) + f(7) = 1 + 1 + 3 + 1 + 5 + 3 + 7 = 21
小易计算这个问题遇到了困难,需要你来设计一个算法帮助他。
输入描述:
输入一个整数N (1 ≤ N ≤ 1000000000)
输出描述:
输出一个整数,即为f(1) + f(2) + f(3)…….f(N)
示例1
输入
7
输出
21
思路
- 奇数的最大奇约数是自身, 偶数的最大奇约数是是除去所有偶因子之后的那个奇数。最直观的思路就是挨个遍历一遍加起来,但是会超时;
- 由于1 ≤ N ≤ 1000000000,不能采用hash表用空间换时间;
- 设sum(i) = f(1) + f(2) + … + f(i):
i 为奇数时,直接求f(i),就是 i 本身,则sum(i) = f(1) + f(2) + … + f(i-1)+i=sum(i-1)+i;
因为要求的是最大奇约数,所以f(2k) = f(k),所以f(2) + f(4) + … + f(2k) = f(1) + f(2) + … + f(k);
i 为偶数时,sum(i) =f(2) + f(4) + … + f(i)+ f(1) + f(3) + … + f(i - 1) = sum(i/2) + 1 + 3 + … + i-1;
可以用递归和非递归方式实现
代码
#include<iostream>
#include<vector>
using namespace std;
long long f(long long n){
long long sum=0;
while(n>0){
if(n%2==0){
sum+=n*n/4;
n/=2;
}
else{
sum+=n;
n--;
}
}
return sum;
}
int main(){
long long n;
cin>>n;
cout<<f(n)<<endl;
return 0;
}
7、买苹果
题目描述
小易去附近的商店买苹果,奸诈的商贩使用了捆绑交易,只提供6个每袋和8个每袋的包装(包装不可拆分)。 可是小易现在只想购买恰好n个苹果,小易想购买尽量少的袋数方便携带。如果不能购买恰好n个苹果,小易将不会购买。
输入描述:
输入一个整数n,表示小易想购买n(1 ≤ n ≤ 100)个苹果
输出描述:
输出一个整数表示最少需要购买的袋数,如果不能买恰好n个苹果则输出-1
示例1
输入
20
输出
3
思路
暴力解方程8*x+6*y=n
代码
#include<iostream>
using namespace std;
int main(){
int n;
cin>>n;
int bag=n;
for(int i=n/8; i>=0; --i){
if((n-8*i)%6==0){
int j=(n-8*i)/6;
bag=(bag>(i+j)?(i+j):bag);
break;
}
}
if(bag==n)
bag=-1;
cout<<bag<<endl;
return 0;
}
8、计算糖果
题目描述
A,B,C三个人是好朋友,每个人手里都有一些糖果,我们不知道他们每个人手上具体有多少个糖果,但是我们知道以下的信息:
A - B, B - C, A + B, B + C. 这四个数值.每个字母代表每个人所拥有的糖果数.
现在需要通过这四个数值计算出每个人手里有多少个糖果,即A,B,C。这里保证最多只有一组整数A,B,C满足所有题设条件。
输入描述:
输入为一行,一共4个整数,分别为A - B,B - C,A + B,B + C,用空格隔开。
范围均在-30到30之间(闭区间)。
输出描述:
输出为一行,如果存在满足的整数A,B,C则按顺序输出A,B,C,用空格隔开,行末无空格。
如果不存在这样的整数A,B,C,则输出No
示例1
输入
1 -2 3 4
输出
2 1 3
思路
A - B = n1
B - C = n2
A + B = n3
B + C = n4
求得A、B、C再判断是否符合条件。
代码
#include<iostream>
using namespace std;
int main() {
int n1, n2, n3, n4;
cin >> n1 >> n2 >> n3 >> n4;
int a, b, c;
a = (n1 + n3) / 2;
b = (n3 - n1) / 2;
c = (n4 - n2) / 2;
if (a-b==n1 && b-c==n2 && a+b==n3 && b+c==n4) {
cout << a << " " << b << " " << c << endl;
}
else {
cout << "No" << endl;
}
return 0;
}