2000年的1月1日,是那一年的第1天。
那么,2000年的5月4日,是那一年的第几天?
数日历、在Excel中输入2000/01/01拖到2000/05/04数行数等均能得到答案。注意2000年是闰年,2月有29天。
125
汉字的字形存在于字库中,即便在今天,16点阵的字库也仍然使用广泛。
16点阵的字库把每个汉字看成是16x16个像素信息。并把这些信息记录在字节中。
一个字节可以存储8位信息,用32个字节就可以存一个汉字的字形了。
把每个字节转为2进制表示,1表示墨迹,0表示底色。每行2个字节,
一共16行,布局是:
第1字节,第2字节
第3字节,第4字节
…
第31字节, 第32字节
这道题目是给你一段多个汉字组成的信息,每个汉字用32个字节表示,这里给出了字节作为有符号整数的值。
题目的要求隐藏在这些信息中。你的任务是复原这些汉字的字形,从中看出题目的要求,并根据要求填写答案。
这段信息是(一共10个汉字):
4 0 4 0 4 0 4 32 -1 -16 4 32 4 32 4 32 4 32 4 32 8 32 8 32 16 34 16 34 32 30 -64 0
16 64 16 64 34 68 127 126 66 -124 67 4 66 4 66 -124 126 100 66 36 66 4 66 4 66 4 126 4 66 40 0 16
4 0 4 0 4 0 4 32 -1 -16 4 32 4 32 4 32 4 32 4 32 8 32 8 32 16 34 16 34 32 30 -64 0
0 -128 64 -128 48 -128 17 8 1 -4 2 8 8 80 16 64 32 64 -32 64 32 -96 32 -96 33 16 34 8 36 14 40 4
4 0 3 0 1 0 0 4 -1 -2 4 0 4 16 7 -8 4 16 4 16 4 16 8 16 8 16 16 16 32 -96 64 64
16 64 20 72 62 -4 73 32 5 16 1 0 63 -8 1 0 -1 -2 0 64 0 80 63 -8 8 64 4 64 1 64 0 -128
0 16 63 -8 1 0 1 0 1 0 1 4 -1 -2 1 0 1 0 1 0 1 0 1 0 1 0 1 0 5 0 2 0
2 0 2 0 7 -16 8 32 24 64 37 -128 2 -128 12 -128 113 -4 2 8 12 16 18 32 33 -64 1 0 14 0 112 0
1 0 1 0 1 0 9 32 9 16 17 12 17 4 33 16 65 16 1 32 1 64 0 -128 1 0 2 0 12 0 112 0
0 0 0 0 7 -16 24 24 48 12 56 12 0 56 0 -32 0 -64 0 -128 0 0 0 0 1 -128 3 -64 1 -128 0 0
解法1: 题目中一个字节可以存储8位信息提示我们需要将数字转化为8位的二进制数,同时观察数据范围都在-128~127之间也提示了这一点。正数的进制转换用循环即可实现,而负数的二进制与正数有很大差异(需要对十进制数 取反后 转换后 取补码)。
解法2: 通过STL中的bitset直接将数字转换为二进制数,倒序遍历bitset(bitset中低位在前面)格式化输出即可。
解法1:
#include
#include
using namespace std;
int main(){
//count用于记录是否需要换行
int x, count = 0;
while(cin >> x){
count = (count + 1) % 2;
//记录是否是负数
bool neg = x < 0;
x = abs(x);
//符号位赋值
int arr[8] = {neg ? 1 : 0};
for(int i = 7; x != 0; i--){
arr[i] = x % 2;
x /= 2;
}
if(neg){
//取补码即取反码后末位+1
//取反码
for(int i = 1; i < 8; i++){
//如果是1变成0,是0变成1
arr[i] = arr[i] ? 0 : 1;
}
//+1
arr[7]++;
for(int i = 7; i >= 1; i--){
arr[i - 1] += arr[i] / 2;
arr[i] %= 2;
}
}
for(int i = 0; i < 8; i++){
if(arr[i]){
cout << "■";
} else {
cout << "□";
}
}
if(!count){
cout << endl;
}
}
}
解法2:
#include
#include
using namespace std;
int main(){
int x, count = 0;
while(cin >> x){
count = (count + 1) % 2;
bitset<8> b(x);
for(int i = 0; i < 8; i++){
if(b[7 - i]){
cout << "■";
} else {
cout << "□";
}
}
if(!count){
cout << endl;
}
}
}
显示出的字符为九的九次方等于多少?,通过计算器算出答案为387420489
如下的10行数据,每行有10个整数,请你求出它们的乘积的末尾有多少个零?
5650 4542 3554 473 946 4114 3871 9073 90 4329
2758 7949 6113 5659 5245 7432 3051 4434 6704 3594
9937 1173 6866 3397 4759 7557 3070 2287 1453 9899
1486 5722 3135 1170 4014 5510 5120 729 2880 9019
2049 698 4582 4346 4427 646 9742 7340 1230 7683
5693 7015 6887 7381 4172 4341 2909 2027 7355 5649
6701 6645 1671 5978 2704 9926 295 3125 3878 6785
2066 4247 4800 1578 6652 4616 1113 6205 3264 2915
3966 5291 2904 1285 2193 1428 2265 8730 9436 7074
689 5510 8243 6114 337 4096 8199 7313 3685 211
末尾有n个零即代表这个数有n个因数10,而10=2*5,即计算出每个整数的因数中有多少个2和5,取两者的最小值即可得出乘积中过多少个因数10。
#include
#include
using namespace std;
int main(){
int x, two = 0, five = 0;
for(int i = 0; i < 100; i++){
cin >> x;
while(x % 2 == 0){
two++;
x /= 2;
}
while(x % 5 == 0){
five++;
x /= 5;
}
}
cout << min(two, five) << endl;
}
答案为 31
x星球的居民脾气不太好,但好在他们生气的时候唯一的异常举动是:摔手机。
各大厂商也就纷纷推出各种耐摔型手机。x星球的质监局规定了手机必须经过耐摔测试,并且评定出一个耐摔指数来,之后才允许上市流通。
x星球有很多高耸入云的高塔,刚好可以用来做耐摔测试。塔的每一层高度都是一样的,与地球上稍有不同的是,他们的第一层不是地面,而是相当于我们的2楼。
如果手机从第7层扔下去没摔坏,但第8层摔坏了,则手机耐摔指数=7。
特别地,如果手机从第1层扔下去就坏了,则耐摔指数=0。
如果到了塔的最高层第n层扔没摔坏,则耐摔指数=n
为了减少测试次数,从每个厂家抽样3部手机参加测试。
某次测试的塔高为1000层,如果我们总是采用最佳策略,在最坏的运气下最多需要测试多少次才能确定手机的耐摔指数呢?
请填写这个最多测试次数。
注意:需要填写的是一个整数,不要填写任何多余内容。
(思路非最优解,最优解思路请参考 https://blog.csdn.net/hebtu666/article/details/84789853)
有序范围查找首先会想到二分查找,二分的次数也就是测试的次数。但是此题增加了限制,一共只有3部手机。当手机耐摔层数为124时,摔3次之后就没有手机可测试了,此时无法确定耐摔指数。因此在保证次数尽可能少的情况下,还需要最大程度的利用手机。
首先思考如果在第i层摔手机,有两种情况:
因为我们不知道耐摔次数是多少,而我们有最坏的运气,所以我们每次肯定会选到需要测试次数最多的情况。而因为我们会采用最优策略,所以我们需要选取一个最优的层数,使在这个层数测试需要的次数最少(虽然在每层都会选取最坏的情况即最多的次数,但我们需要在不同层的最坏情况之间选取一个最坏中的最好情况)。
进一步思考,当手机数量为1时,为了保证能测出耐摔指数,必须从第1层测试到最后一层,因此当手机数为1时测试次数是已知的,次数等于层数。
因为我们不知道在哪一层测试是最优策略,所以每一层都计算一下次数,在这些次数取最小值就可以视为选取了最优策略。
针对手机摔坏了的情况,此时可以看为测试层数为i-1层,有k-1部手机的题目。
针对手机没有摔坏的情况,此时可以看为测试层数为1000-i层,有k部手机的题目。
因为测试层数恒减,且手机数为1时的测试次数已知,所以必定有解。可以使用暴力递归,这里使用动态规划求解,代码思路见注释。
#include
#define HEIGHT 1000
#define NUM 3
using namespace std;
int main(){
//dp[i][j]代表在有i部手机,层数范围为j的情况下需要试多少次,有3部手机层数范围为1000(1-1000)的情况下是需要试的次数为dp[3][1000]
int dp[NUM + 1][HEIGHT + 1] = {};
//如果只有一部手机,那么必须从范围内的第一层开始一层一层试,最坏情况在最后一层试出结果,所以i层范围要试i次
for(int i = 1; i <= HEIGHT; i++){
dp[1][i] = i;
}
//DP有两部手机和三部手机的情况
for(int i = 2; i <= NUM; i++){
for(int j = 1; j <= HEIGHT; j++){
//对于有i部手机层数范围为j的情况
int min_value = -1;
//假设从范围内的第k层摔
for(int k = 1; k <= j; k++){
//第一种情况摔坏了,此时范围缩小至1~k-1,手机数量-1,试的次数为 dp[i - 1][k - 1](不包括这次,假设)
//第二种情况没摔坏,此时范围缩小k+1~j,次数为 dp[i][j - k]
//因为最坏的运气,所以取这两种情况次数的较大值
int max_count = max(dp[i - 1][k - 1], dp[i][j - k]);
if(min_value != -1 && min_value < max_count){
min_value = max_count;
}
}
//因为最优策略,所以取在所有层试的次数的最小值,之后加上这次摔的次数
dp[i][j] = min_value + 1;
}
}
cout << dp[NUM][HEIGHT] << endl;
}
运行得出答案为 19
以下代码可以从数组a[]中找出第k小的元素。
它使用了类似快速排序中的分治算法,期望时间复杂度是O(N)的。
请仔细阅读分析源码,填写划线部分缺失的内容。
#include
int quick_select(int a[], int l, int r, int k) {
int p = rand() % (r - l + 1) + l;
int x = a[p];
{int t = a[p]; a[p] = a[r]; a[r] = t;}
int i = l, j = r;
while(i < j) {
while(i < j && a[i] < x) i++;
if(i < j) {
a[j] = a[i];
j--;
}
while(i < j && a[j] > x) j--;
if(i < j) {
a[i] = a[j];
i++;
}
}
a[i] = x;
p = i;
if(i - l + 1 == k) return a[i];
if(i - l + 1 < k) return quick_select( _____________________________ ); //填空
else return quick_select(a, l, i - 1, k);
}
int main()
{
int a[] = {1, 4, 2, 8, 5, 7, 23, 58, 16, 27, 55, 13, 26, 24, 12};
printf("%d\n", quick_select(a, 0, 14, 5));
return 0;
}
首先分析代码:
#include
#include
//a[]为要查找的数组,l为查找起始位置,r为查找末位(包含位置r),k为查找第几小的数
int quick_select(int a[], int l, int r, int k) {
//从[l, r)区间中选取一个随机数
int p = rand() % (r - l + 1) + l;
int x = a[p];
//将p位置的值和末位的值交换
{int t = a[p]; a[p] = a[r]; a[r] = t;}
int i = l, j = r;
//将所有值小于x的元素尽可能的换到左边,大于的换到右边
//当p=4,此时数组为{1, 4, 2, 8, 12, 7, 23, 58, 16, 27, 55, 13, 26, 24, 5},可以看到4位的5和末位的12互换了位置
while(i < j) {
while(i < j && a[i] < x) i++;
if(i < j) {
a[j] = a[i];
j--;
}
while(i < j && a[j] > x) j--;
if(i < j) {
a[i] = a[j];
i++;
}
}
//此时数组为{1, 4, 2, 8, 12, 7, 23, 58, 16, 27, 55, 13, 26, 24, 8},i的值为3,a[i] = 8,可以看到[l, i)区间内的值都小于5,[i, r]区间内的值都大于5,8由于上面循环中的赋值出现了两次,而a[i]位置的应该是x(5)的位置
//循环结束后i应该是第一个大于x元素的下标,也就是说得到了x是这个区间中第几小的元素
a[i] = x;
p = i;
//此时i - l + 1 = 4,代表5是[l, r]区间中第4小的元素
//i - l + 1代表a[i]是第几小的元素,如果和要求的相等则返回
if(i - l + 1 == k) return a[i];
//此时已知5是整个区间中第4小的元素,那么要求第5小的元素,就要在较大数字的区间找下一个第1小的元素
//如果比要求的小,则在更大的数字区间内继续查找,因为a[i]已知比要找的排名低,所以从i + 1开始到r,因为已知a[i]是第i - l + 1小的数字,所以剩余区间的起始点从k - (i - l + 1)开始
if(i - l + 1 < k) return quick_select( a, i + 1, r, k - (i - l + 1) ); //填空
//比要求的大就从较小的区间查找
else return quick_select(a, l, i - 1, k);
}
int main()
{
int a[] = {1, 4, 2, 8, 5, 7, 23, 58, 16, 27, 55, 13, 26, 24, 12};
printf("%d\n", quick_select(a, 0, 14, 5));
return 0;
}
观察代码可以发现函数流程大致为随机选取一个数,将所有大于这个数的元素放到它右边,小于的放到左边,这样这个元素的位置就代表它是第几小的元素,之后根据结果缩小需要判断的区间范围,最终得到要求的第x小的元素。
题目中同时提到了期望时间复杂度是O(N),最优情况下第一次随机就随机到想要查询的数,时间复杂度为O(N),符合题目要求。
答案为 a, i + 1, r, k - (i - l + 1)
给定三个整数数组
A = [A1, A2, … AN],
B = [B1, B2, … BN],
C = [C1, C2, … CN],
请你统计有多少个三元组(i, j, k) 满足:
【输入格式】
第一行包含一个整数N。
第二行包含N个整数A1, A2, … AN。
第三行包含N个整数B1, B2, … BN。
第四行包含N个整数C1, C2, … CN。
对于30%的数据,1 <= N <= 100。
对于60%的数据,1 <= N <= 1000。
对于100%的数据,1 <= N <= 100000,0 <= Ai, Bi, Ci <= 100000。
【输出格式】
一个整数表示答案
【样例输入】
3
1 1 1
2 2 2
3 3 3
3
1 2 3
2 3 5
3 4 5
【样例输出】
27
7
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
首先思考暴力思路,针对每个三元组验证是否符合要求,此时三层嵌套循环时间复杂度为O(N3),最大循环次数为1015,很明显超出时间限制。
进一步思考,当三元组中Bj、Ck确定时,找到符合条件的尽可能大的Ai,则所有小于等于Ai的三元组也同样符合要求。因此可以对数组A排序,而在排序后的数组中使用二分查找时间复杂度最低。此时两层循环遍历B、C时间复杂度O(N2),循环内二分查找A时间复杂度O(logN),总时间复杂度为O(N2logN),最大循环次数约为5*105(数组A排序)+5*1010(遍历BC并二分查找A)≈5*1010,依然超出时间限制。
此时三元组中Ai的选择已经确定,优化重点主要放在对数组B、C的遍历上。仔细思考后发现,**当三元组中Bj、Ck确定时,找到符合条件的尽可能大的Ai**只与Bj的值有关(确定时Bj、Ck已经符合条件),同理Ck的值也只与Bj有关。因此同理对数组C排序后二分查找符合条件的尽可能小的Ck,则所有大于等于Ck的三元组也同样符合要求。此时遍历B时间复杂度O(N),循环内二分查找A、C时间复杂度O(logN),总时间复杂度为O(NlogN),最大循环次数约为2*5*105(数组A、C排序)+105*2*5(遍历B并每次对A、C二分查找)= 2*106,符合时间限制。
符合条件的三元组最大数量约为1015,因此结果需要使用long long存储。
algorithm头文件中的lower_bound函数和upper_bound函数已经写好了二分查找第一个比某数小/大的位置的函数实现,调用即可,参数1为数组首地址,参数2为数组尾地址,参数3为需要查找的数。
#include
#include
using namespace std;
int main(){
int n;
while(cin >> n){
int a[100001], b[100001], c[100001];
for(int i = 0; i < n; i++){
cin >> a[i];
}
for(int i = 0; i < n; i++){
cin >> b[i];
}
for(int i = 0; i < n; i++){
cin >> c[i];
}
//对a、c排序(默认升序)
sort(a, a + n);
sort(c, c + n);
long long result = 0;
//遍历b
for(int i = 0; i < n; i++){
//返回的是元素地址,需要减去首地址确定位置,因为已排序所以位置也就是满足条件的元素个数
long long a_count = lower_bound(a, a + n, b[i]) - a;
long long c_count = n - (upper_bound(c, c + n, b[i]) - c);
result += a_count * c_count;
/* 自己实现的二分查找
int start = 0, end = n - 1, mid;
//二分查找a
while(start <= end){
mid = (start + end) / 2;
if(a[mid] >= b[i]){
end = mid - 1;
} else {
start = mid + 1;
}
}
//此时下标位置就代表有多少个元素满足条件,下标从0开始所以+1
long long a_count = end + 1;
//二分查找c
start = 0, end = n - 1;
while(start <= end){
mid = (start + end) / 2;
if(c[mid] <= b[i]){
start = mid + 1;
} else {
end = mid - 1;
}
}
result += a_count * (n - (end + 1));
*/
}
cout << result << endl;
}
}
如图所示的螺旋折线经过平面上所有整点恰好一次。
对于整点(X, Y),我们定义它到原点的距离dis(X, Y)是从原点到(X, Y)的螺旋折线段的长度。
例如dis(0, 1)=3, dis(-2, -1)=9
给出整点坐标(X, Y),你能计算出dis(X, Y)吗?
【输入格式】
X和Y
对于40%的数据,-1000 <= X, Y <= 1000
对于70%的数据,-100000 <= X, Y <= 100000
对于100%的数据, -1000000000 <= X, Y <= 1000000000
【输出格式】
输出dis(X, Y)
【样例输入】
0 1
【样例输出】
3
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
稍后更新。
小明维护着一个程序员论坛。现在他收集了一份”点赞”日志,日志共有N行。其中每一行的格式是:
ts id
表示在ts时刻编号id的帖子收到一个”赞”。
现在小明想统计有哪些帖子曾经是”热帖”。如果一个帖子曾在任意一个长度为D的时间段内收到不少于K个赞,小明就认为这个帖子曾是”热帖”。
具体来说,如果存在某个时刻T满足该帖在[T, T+D)这段时间内(注意是左闭右开区间)收到不少于K个赞,该帖就曾是”热帖”。
给定日志,请你帮助小明统计出所有曾是”热帖”的帖子编号。
【输入格式】
第一行包含三个整数N、D和K。
以下N行每行一条日志,包含两个整数ts和id。
对于50%的数据,1 <= K <= N <= 1000。
对于100%的数据,1 <= K <= N <= 100000,0 <= ts <= 100000,0 <= id <= 100000。
【输出格式】
按从小到大的顺序输出热帖id。每个id一行。
【输入样例】
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
【输出样例】
1
3
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
题目要对每个id判断是否存在区间使区间内的点赞数大于等于k,所以不能直接暴力。此时应该想到通过模拟时间的区间,统计区间内的点赞数量来解题。要通过模拟统计时间区间,首先需要根据时间进行排序,之后用两个变量模拟区间内点赞的记录下标范围,当记录离开区间时删除点赞记录,而当有记录进入区间时判断是否达到点赞数记录即可,总时间复杂度为O(NlogN),代码思路见注释。
#include
#include
using namespace std;
struct Log {
int time, id;
};
bool cmp(Log a, Log b){
return a.time < b.time;
}
int main(){
int n, d, k;
cin >> n >> d >> k;
Log logs[100001];
for(int i = 0; i < n; i++){
cin >> logs[i].time >> logs[i].id;
}
//按照时间从小到大排序点赞日志
sort(logs, logs + n, cmp);
//用两个下标维护一个区间
int start_index = 0, end_index = 0;
//like_num数组记录维护的区间内每个帖子的点赞数
int like_num[100001] = {};
//hot数组记录帖子是否已成为热帖
bool hot[100001] = {};
while(end_index < n){
//如果区间长度超过规定范围
while(logs[end_index].time - logs[start_index].time >= d){
//首先减去区间外的点赞数
like_num[logs[start_index].id]--;
//区间左端向前移动
start_index++;
}
//加上区间右端新增的点赞
like_num[logs[end_index].id]++;
//如果达到了热帖标准
if(like_num[logs[end_index].id] == k){
//修改标志位
hot[logs[end_index].id] = true;
}
//往后推进
end_index++;
}
//遍历所有id,从小到大输出
for(int i = 0; i < 100001; i++){
if(hot[i]){
cout << i << endl;
}
}
}
你有一张某海域NxN像素的照片,".“表示海洋、”#"表示陆地,如下所示:
.......
.##....
.##....
....##.
..####.
...###.
.......
其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。
例如上图中的海域未来会变成如下样子:
.......
.......
.......
.......
....#..
.......
.......
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
【输入格式】
第一行包含一个整数N。 (1 <= N <= 1000)
以下N行N列代表一张海域照片。
照片保证第1行、第1列、第N行、第N列的像素都是海洋。
【输出格式】
一个整数表示答案。
【输入样例】
7
.......
.##....
.##....
....##.
..####.
...###.
.......
【输出样例】
1
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
要想知道有多少岛屿会淹没,首先需要知道岛屿的数量。很明显是图的遍历,因为要分辨不同的岛屿,所以对一个岛屿遍历时要遍历所有的陆地节点,因此深搜和广搜时间相同。在区分某节点是否属于已遍历过的岛屿方面,可以使用标志位记录是否访问过。
那么一个岛屿被完全淹没的条件即岛屿内任何一个节点的相邻节点都有海洋。换言之如果存在一个节点的相邻节点没有海洋,这个岛屿就不会被淹没。所以在遍历岛屿节点时顺便判断每个节点的相邻节点是否不存在海洋,如果存在相邻节点没有海洋的节点则这个节点不计数,那么剩下的就是被完全淹没的岛屿数量了。
#include
using namespace std;
int n;
char map[1001][1001];
bool visited[1001][1001] = {};
int count = 0;
bool disappearIsland = true;
//判断i,j位置的节点是否是海洋
bool isSea(int i, int j){
if(i < 0 || i >= n || j < 0 || j >= n || map[i][j] == '#'){
return false;
}
return true;
}
void dfs(int i, int j){
if(i < 0 || i >= n || j < 0 || j >= n || visited[i][j] || map[i][j] == '.'){
return;
}
visited[i][j] = true;
//如果四个方向都不是海洋,即这个陆地不会被淹没,就说明这个岛屿还有陆地
if(!isSea(i + 1, j) && !isSea(i - 1, j) && !isSea(i, j + 1) && !isSea(i, j - 1)){
//这个岛屿不会消失
disappearIsland = false;
}
//四个方向遍历
dfs(i + 1, j);
dfs(i - 1, j);
dfs(i, j + 1);
dfs(i, j - 1);
}
int main(){
cin >> n;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
cin >> map[i][j];
}
}
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(map[i][j] == '#' && !visited[i][j]){
//默认这个岛屿会消失
disappearIsland = true;
dfs(i, j);
//如果遍历完了确实没有陆地存在了
if(disappearIsland){
::count++;
}
}
}
}
cout << ::count << endl;
}