小易来到了一条石板路前,每块石板上从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
方法一:
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstring>
#include <string>
#include <cmath>
using namespace std;
void produceDiv(int k);
void BFS();
const int maxn = 100010;
bool vis[maxn];
vector<int> myHash[maxn];
queue<int> que;
int step[maxn];
int N, M;
/**
思路:用BFS中间稍微优化一点
1.求一个数n的约数时间复杂度为根号n
2.BFS的时候用vis判断是否重复访问
特殊情况N == M
**/
int main()
{
cin >> N >> M;
if(N == M) {
printf("0\n");
return 0;
}
memset(vis, false, sizeof(vis));
memset(step, 0, sizeof(step));
step[N] = 0;
produceDiv(N);
que.push(N);
BFS();
return 0;
}
void BFS() {
while(!que.empty()) {
int k = que.front();
que.pop();
for(unsigned i = 0; i < myHash[k].size(); i++) {
int newK = k + myHash[k][i];
if(newK <= M && !vis[newK]) {
produceDiv(newK);
step[newK] = step[k] + 1;
que.push(newK);
vis[newK] = true;
if(newK == M) {
printf("%d\n", step[newK]);
return;
}
}
}
}
printf("-1\n");
}
void produceDiv(int k) {
/*
O(n)的时间复杂度会超时
for(int i = 2; i < k; i++) {
if(k%i == 0) {
myHash[k].push_back(i);
}
}
*/
int mid = (int)sqrt(k)+1;
for(int i = 2; i < mid; i++) {
if(k%i == 0) {
myHash[k].push_back(i);
myHash[k].push_back(k/i);
}
}
}
/**
测试数据:
10 100000
**/
方法二:
采用动态规划思想求解。创建一个vector容器steps,steps[i]表示到达i号石板所需的最小步数。初始化为steps容器为INT_MAX。从序号N的石板开始逐个遍历,若steps[i]为INT_MAX,表示该点不可到达,直接开始下次循环。若steps[i]不为INT_MAX,表示该点可以到达,下面求解编号i的约数,进行动态规划。动态规划的转移方程为
steps[i+j] = min(steps[i]+1,steps[i+j]) //i为石板编号,j为i的约束
steps[N] = 0
#include <iostream>
#include <vector>
#include <climits>
#include <cmath>
#include <algorithm>
using namespace std;
int main() {
int N, M;
while(cin >> N >> M) {
vector<int> steps(M+1,INT_MAX);
steps[N] = 0;
for(int i = N; i <= M; i++) {
if(steps[i] == INT_MAX) {
continue;
}
for(int j=2; (j*j) <= i; j++){
if(i%j == 0) {
if(i+j <= M){
steps[i+j] = min(steps[i]+1, steps[i+j]);
}
if(i+(i/j) <= M){
steps[i+(i/j)] = min(steps[i]+1, steps[i+(i/j)]);
}
}
}
}
if(steps[M] == INT_MAX){
steps[M] = -1;
}
cout << steps[M] << endl;
}
return 0;
}
回文序列
时间限制:1秒
空间限制:32768K
如果一个数字序列逆置之后跟原序列是一样的就称这样的数字序列为回文序列。例如:
{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),以空格分隔。
输出描述:
输出一个数,表示最少需要的转换次数
例如:
输入:
4
1 1 1 3
输出:
2
思路:
使用双端队列deque数据结构进行求解。双端队列deque数据结构支持高效地首尾两端元素的插入和删除。
本题思路为:判断队首和队尾元素。若二者相等,则将这两个元素都弹出队列,将队列规模缩小2个,再对该问题进行判断;若二者不相等,则选择其中较小的一个,将该元素和与其相邻的元素都弹出队列,再将其和插入队列,从而将队列规模缩小1个,再对该问题进行判断。
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
#include <deque>
using namespace std;
int n;
deque<int> mydeq;
int work() {
int cnt = 0;
while(true) {
if(mydeq.size() == 1 || mydeq.empty()) {
break;
}
int frontNum = mydeq.front();
int backNum = mydeq.back();
if(frontNum == backNum) {
mydeq.pop_front();
mydeq.pop_back();
} else if(frontNum < backNum) {
int first = mydeq.front();
mydeq.pop_front();
int second = mydeq.front();
mydeq.pop_front();
mydeq.push_front(first + second);
cnt++;
} else if(frontNum > backNum) {
int last = mydeq.back();
mydeq.pop_back();
int second_last = mydeq.back();
mydeq.pop_back();
mydeq.push_back(last + second_last);
cnt++;
}
}
return cnt;
}
int main()
{
scanf("%d", &n);
int read;
for(int i = 0; i < n; i++) {
scanf("%d", &read);
mydeq.push_back(read);
}
int res = work();
cout << res << endl;
return 0;
}
/**
c.push_back(num)在末尾位置插入元素
c.pop_back()删除末尾位置的元素
c.push_front(num)在开头位置插入元素
c.pop_front()删除开头位置的元素
c.front()返回c容器的第一个元素
c.back()返回c容器的最后一个元素
**/
方法二:
保存状态进行BFS超内存
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <set>
#include <queue>
using namespace std;
set< vector<int> > mySet;
queue<vector<int> > que;
int n;
bool is_ok(vector<int> v) {
if(v.size() == 1) return true;
unsigned Size = v.size();
int left = 0;
int right = Size-1;
while(left != right && left<right) { /// [2 2]
if(v[left] == v[right]) {
left++;
right--;
continue;
}else {
return false;
}
}
return true;
}
void build_newVector(vector<int> &myVector, int index, vector<int> &v) {
for(int i = 0; i < (int)myVector.size(); i++) {
if(i == index) {
v.push_back(myVector[i] + myVector[i+1]);
} else {
if(i == index+1) continue;
else {
v.push_back(myVector[i]);
}
}
}
}
int main()
{
scanf("%d", &n);
vector<int> V;
int read;
for(int i = 0; i < n; i++) {
scanf("%d", &read);
V.push_back(read);
}
que.push(V);
while(!que.empty()) {
vector<int> myVector = que.front();
que.pop();
bool res = is_ok(myVector);
if(res) {
printf("%d\n", n-myVector.size());
break;
}
unsigned Size = myVector.size();
vector<int> newV;
newV.clear();
for(unsigned i = 0; i <= Size-2; i++) {
build_newVector(myVector, i, newV);
set<vector<int> >::iterator it = mySet.find(newV);
if(it != mySet.end()) ;
else {
que.push(newV);
mySet.insert(newV);
}
newV.clear();
}
}
return 0;
}
优雅的点
时间限制:1秒
空间限制:32768K
小易有一个圆心在坐标原点的圆,小易知道圆的半径的平方。小易认为在圆上的点而且横纵坐标都是整数的点是优雅的,小易现在想寻找一个算法计算出优雅的点的个数,请你来帮帮他。
例如:半径的平方如果为25
优雅的点就有:(+/-3, +/-4), (+/-4, +/-3), (0, +/-5) (+/-5, 0),一共12个点。
输入描述:
输入为一个整数,即为圆半径的平方,范围在32位int范围内。
输出描述:
输出为一个整数,即为优雅的点的个数
思考:
1。n开根号为整数的情况和不为整数的情况
2。考虑圆的对称性
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
int n;
int R;
int main()
{
scanf("%d", &n);
R = (int)sqrt(n);
long long xx, yy, y;
int cnt = 0;
//n开根号为整数
if(R*R == n) {
for(long long i = 0; i < R; i++) {
xx = i*i;
yy = n - xx;
y = (long long)sqrt(yy);
if(n-xx == y*y) {
cnt++;
}
}
//n开根号为不为整数
} else {
for(long long i = 0; i <= R; i++) {
xx = i*i;
yy = n - xx;
y = (long long)sqrt(yy);
if(n-xx == y*y) {
cnt++;
}
}
}
cnt = cnt*4;
cout << cnt << endl;
return 0;
}
/**
测试数据:
18
4
365
16
**/
数字翻转
时间限制:1秒
空间限制:32768K
对于一个整数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))的值
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <sstream>
using namespace std;
string int2string(int x) {
ostringstream stream;
stream << x ;
return stream.str();
}
int string2int(string str) {
unsigned index = 0;
while(index < str.length() && str[index]=='0') {
index++;
}
int sum = 0;
for(unsigned i = index; i < str.length(); i++) {
sum = sum * 10 + str[i]-'0';
}
return sum;
}
int rev(int x) {
string str = int2string(x);
reverse(str.begin(), str.end());
return string2int(str);
}
int main()
{
int x, y;
scanf("%d%d", &x, &y);
cout << rev(rev(x) + rev(y)) << endl;
return 0;
}
买苹果
时间限制:1秒
空间限制:32768K
小易去附近的商店买苹果,奸诈的商贩使用了捆绑交易,只提供6个每袋和8个每袋的包装(包装不可拆分)。 可是小易现在只想购买恰好n个苹果,小易想购买尽量少的袋数方便携带。如果不能购买恰好n个苹果,小易将不会购买。
输入描述:
输入一个整数n,表示小易想购买n(1 ≤ n ≤ 100)个苹果
输出描述:
输出一个整数表示最少需要购买的袋数,如果不能买恰好n个苹果则输出-1
思路:此题数据量比较小,状态也很少, 可以用DFS。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <sstream>
using namespace std;
void dfs(int num, int bagNum);
int n;
bool is_ok = false;
int main()
{
cin >> n;
dfs(0, 0);
if(!is_ok) {
cout << "-1" << endl;
}
return 0;
}
//num为苹果的个数, bagNum为袋子个数
void dfs(int num, int bagNum) {
if(is_ok) return;
if(num > n) return;
if(num == n) {
printf("%d\n", bagNum);
is_ok = true;
return;
}
dfs(num+8, bagNum+1);
dfs(num+6, bagNum+1);
}
[编程题] 计算糖果
时间限制:1秒
空间限制:32768K
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
思考:简单题。
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int main()
{
int x, y, z, k;
int A, B, C;
cin >> x >> y >> z >> k;
A = (x+z)/2;
B = (y+k)/2;
C = (k-y)/2;
if(A-B == x && B-C == y && A+B == z && B+C==k) {
cout << A << " " << B << " " << C << endl;
} else {
printf("No\n");
}
return 0;
}