1.数的分解
描述
这是一道 Special Judge 的题目,只要输出符合题目要求的答案,那么你的程序就会被认为是正确的。(等等这不是废话吗?!)
给你一个正整数 n,你能否把 n 分解成 一个素数 与 一个合数 的和。
输入
第一行是一个正整数 T 代表测试案例的数量。(1 <= T <= 100000)
每组测试案例是一个正整数 n 。(1 <= n <= 100000)
输出
如果你可以把 n 分解成 一个素数 与 一个合数 的和,那么输出这两个数,并以空格隔开;如果不能,则输出-1。
每组案例输出结束以后都要换行。
样例输入
2
3
11
样例输出
-1
5 6
HINT
把 11 分解成 9 和 2,你的答案也会被认为是正确的。
先输出合数再输出素数不会影响答案的正确性。
代码:
#include<iostream>
#include<cmath>
using namespace std;
bool isPrime(int m)
{
if (m < 2)
{
return false;
}
for (int i = 2; i<=sqrt(m); i++)
{
if (m % i == 0)
return false;
}
return true;
}
int main()
{
int n;
cin >> n;
while (n--)
{
int m,a;
cin >> m;
if (m <= 5)
{
cout << -1;
}
else
{
a = m;
while (m--)
{
if (isPrime(m) && !isPrime(a - m)&&(a-m)>1)
{
break;
}
}
cout << m << " " << a - m;
}
cout << endl;
}
return 0;
}
总结:先发现一个事实:若m<6,则不可能找到任何一个满足题意的情况(因为最小的素数为2,最小的和数为4);而若m>=6,则一定能找到一个满足题意的情况。
在循环开始之前,先将m的值赋给a,接着再用循环判断:让m不断地自减1,直到满足m为素数的同时(a-m)为和数的情况break,减少运行时间;同时,应注意和数的判断:(a-m)不为素数且(a-m)的值大于1。
2.约会时间
描述
在数字里,只有0、1、6、8、9倒过来看还是数字,分别变成了0、1、9、8、6。
有一对情侣Alice和Bob用纸条约定明天见面的时间。他们约会的合理时间只会在a点b分到c点d分之间(包含两端)。
Alice写好时间向Bob传递纸条,Bob拿反了,看成了另一个不同的时间。问Alice写的时间有多少种不同的可能性?
已知Alice不会写错时间,并且写的时间一定会在合理时间范围内(a点b分到c点d分之间)
Bob也足够聪明如果有数字反过来根本不是数字,就会发现纸条拿反了,从而不会看错;如果反过来看的时间不在合理时间范围内,也会意识到拿反了纸条,从而不会看错。
Alice和Bob事先有约定,只用时和分来表示时间,而且即使时或分只有一位数,也要在前面补0成两位数;并且用24小时制的时间(从00:00到23:59)。
例如下午3点5分,他们会表示成15:05;凌晨0点8分,他们会表示成00:08。
输入
4个整数a、b、c、d,含义见【描述】。
保证a点b分和c点d分是24小时制的合法时间,而且c点d分一定是a点b分之后的时刻。
输出
一个整数,表示Alice有多少种合理的时间写法。
不要换行。
样例输入
11 8 11 12
样例输出
0
HINT
样例中,11:08到11:12之间,11:08倒过来是80:11不是合法时间;11:09倒过来是60:11不是合法时间;11:10倒过来是01:11是合法时间,但不在11:08到11:12之间;11:11倒过来是11:11是合法时间也在11:08到11:12之间,但与原时间一样,不满足倒过来是不同时间的要求;11:12倒过来不是数字。所以都不满足要求。
代码:
#include<iostream>
#include<cmath>
using namespace std;
int f(int a)//返回一个数翻转后的结果
{
string a1;
while (a > 0)
{
a1 += a % 10 + '0';
a /= 10;
}
if (a1.length() < 2)
{
a1 = a1+'0';
}
int ans = 0;
for (int i = 0; i < a1.length(); i++)
{
ans += (a1[i]-'0')*pow(10 , a1.length() - 1 - i);
}
return ans;
}
bool isLegaltime(int a, int b)
{
if (a == 0)
{
if (b == 1)
return true;
else if (b == 10)
return true;
else if (b == 11)
return true;
else
return false;
}
else if (a == 1)
{
if (b == 0)
return true;
else if (b == 1)
return true;
else if (b == 11)
return true;
else
return false;
}
else if (a == 10)
{
if (b == 0)
return true;
else if (b == 10)
return true;
else if (b == 11)
return true;
else
return false;
}
else if (a == 11)
{
if (b == 0)
{
return true;
}
else if (b == 1)
{
return true;
}
else if (b == 10)
{
return true;
}
else
{
return false;
}
}
return false;
}
int main()
{
int a, b, c, d, cnt = 0;
cin >> a >> b >> c >> d;
int start = a * 60 + b, end = c * 60 + d;
while (a==c&&b<=d||a<c)
{
if (isLegaltime(a, b))
{
int a1 = f(b), b1 = f(a);
if (a1 * 60 + b1 >= start && a1 * 60 + b1 <= end)
{
cnt++;
//cout << "符合时a=" << a << " " << "b=" << b << endl;
//cout << "翻转后a1=" << a1 << " " << "b1=" << b1 << endl;
//cout << endl;
}
}
b++;
if (b > 60)
{
a++;
b = 0;
}
//cout << "a=" << a << " " << "b=" << b << endl;
}
cout << cnt;
return 0;
}
3.广播操
描述
广播操的口令是12345678 22345678 32345678 … 82345678,然后下一节继续这样的循环,就像是一个无线循环的数列。好奇的火华想知道这个无穷无尽的数列中的第x个数字是几。
输入
一个正整数n,表示测试案例的数量。
每组案例由一个正整数x组成,x不大于1e+9,含义见【描述】。
输出
针对每组案例,输出一个一位整数,表示数列第x个数字。
每组案例输出完都要换行。
样例输入
2
10
65
样例输出
2
1
HINT
第10个数字是22345678中的第二个2,第65个数字是第二轮循环中12345678的数字1。
代码:
#include<iostream>
using namespace std;
int main()
{
int n;
cin >> n;
while (n--)
{
int m;
cin >> m;
if (m > 64)
{
m %= 64;
}
if (m % 8 == 1)
{
cout << m / 8 + 1;
}
else if (m % 8 == 0)
{
cout << 8;
}
else
{
cout << m % 8;
}
cout << endl;
}
return 0;
}
总结:要点就在于将大于64的数字变为64以内的数字。
即:若m>64,则应立刻让m对64取模,使m的范围永远在64这一个循环内,这是最重要的!
其次便是找规律:若m是8的倍数,则则输出8;若m对8取模的结果为1,则输出m/8+1;其他则是输出m对8取模的结果。
因此,以后做到类似题目——在某一个范围内规律变化的数列,第一步便是要控制m的值在循环范围之内!
4.平方和
描述
已知一个正整数x,问x能否凑成三个互不相同的正整数的平方和。
输入
一个正整数n,表示测试案例的数量。
每组案例由一个正整数x组成(x不大于1e+8)。
输出
针对每组案例,如果x可以表示成三个互不相同的正整数的平方和,那么输出Yes,否则输出No。
每组案例输出完都要换行。
样例输入
2
30
10
样例输出
Yes
No
#include<iostream>
#include<cmath>
using namespace std;
bool f(int x)
{
for (int i = 1; i * i <= x / 3; i++)
{
for (int j = i + 1; j * j <= (x - i * i) / 2; j++)
{
int m = x - i * i - j * j;
int k = sqrt(m);
if (k * k == m)
{
return true;
}
}
}
return false;
}
int main()
{
int n;
cin >> n;
while (n--)
{
int x;
cin >> x;
if (f(x))
cout << "Yes";
else
cout << "No";
cout << endl;
}
return 0;
}
5.等差数列
描述
已知等差数列的前三项,问数列中首个值大于x的项是第几项?
输入
一个正整数n,表示有n组测试案例。
每组案例由4个整数a、b、c、x表示,范围均在-5e+8到5e+8之间。
其中a、b、c是数列中的第1项、第2项、第3项。x的含义见【描述】。
输出
针对每组案例,输出一个整数y,表示数列中首个值大于x的项是第y项。如果数列中没有任何项能大于x,则输出-1。
每组案例输出完都要换行。
样例输入
3
4 5 6 7
9 7 5 10
10 20 30 15
样例输出
5
-1
2
#include<iostream>
using namespace std;
int main()
{
int n;
cin >> n;
while (n--)
{
long long int a, b, c,x;
cin >> a >> b >> c>>x;
if (x >= a)
{
if (a - b >= 0)
{
cout << -1;
}
else
{
cout << (x + 2 * b - 3 * a) / (b - a);
}
}
else
{
cout << 1;
}
cout << endl;
}
return 0;
}
总结:先判断x的值是否大于首项:若x<a,则直接输出1(因为此时首项a就是第一个大于x的项);若x>=a,分为递增与递减两种情况:若为递减数列,因为第一项就小于x,所以永远也不可能有一项大于x,此时直接输出-1;若为递减数列,则应通过方程x+(b-a)=a+(n-1)*(b-a)得到n的通项公式,获得第一位大于x的项所对应的项数。
6.吐泡泡
描述
小鱼儿,吐泡泡,吐了 n 个小泡泡。我们规定这些小泡泡的大小都是 1 ,并且每两个大小相同的泡泡都可以合成一个更大的泡泡,例如:两个大小为 1 的泡泡可以变成一个大小为 2 的泡泡,两个大小为 2 的泡泡可以变成一个大小为 4 的泡泡,…,以此类推。现在我想知道这 n 个小泡泡最后会变成多少个泡泡。
输入
第一行是一个正整数 T 代表测试案例的数量。(1 <= T <= 1e5)
从第二行到第 T+1 行,每行有一个正整数 n 代表小鱼儿吐了 n 个小泡泡。(1 <= n <= 1e18)
输出
针对每组案例,输出这 n 个小泡泡最后会变成多少个泡泡,然后换行。
样例输入
2
5
8
样例输出
2
1
HINT
5个小泡泡最后会变成一个大小为 4 的泡泡和一个大小为 1 的泡泡。
8个小泡泡最后会变成一个大小为 8 的泡泡。
#include<iostream>
using namespace std;
int main()
{
long long int n;
cin >> n;
while (n--)
{
long long int a,cnt=0;
cin >> a;
while (a)
{
if (a % 2 != 0)
{
cnt++;
}
a /= 2;
}
cout << cnt << endl;
}
return 0;
}
总结:找规律——当泡泡的总数为奇数时,总有一个泡泡无法与其他泡泡合并,而其他的泡泡每两个合并,因此泡泡的总数就成为原来的一半;若泡泡为偶数,则最后一定只能合成一个泡泡。