目录
试题A:空间
#include <iostream>
using namespace std;
int main()
{
// 1MB=1024KB 1KB=1024字节(byte) 1byte=8位
cout<<(long long)256*1024*1024*8/32;
return 0;
}
答案是: 67108864
试题B:卡片
思路:模拟
具体看代码注解(非常详细)
答案是: 3181
#include<bits/stdc++.h>
using namespace std;
int s[10]; // 存储0~9每一个数字的个数
bool check(int x){
// 枚举x的每一位数字
while(x){
int t=x%10; // 取出x的各位数字
x/=10; // 去掉个位数字,比如 201/10=20
// 先将t这个数字的个数减一,然后判断是否还有t这个数字,如果没有则返回false
if(--s[t]<0) return false;
}
return true;
}
int main(){
for(int i=0;i<10;i++) s[i]=2021; // 初始时每一个数字都有2021个
// 从前往后依次枚举每一个数
for(int i=1;;i++){
// 如果第i个数不能拼出来,那么拼出来的数应该是i-1
if(!check(i)){
cout<<i-1; // 3181
return 0;
}
}
return 0;
}
试题C:直线
思路:模拟
定义一个结构体存储每条直线的斜率和截距,枚举所有点对,如果斜率存在(x1!=x2),则求出当前的斜率k,和截距b,加入到结构体数组中,然后将数组按斜率截距排序,如果相邻的两个直线的斜率不同或者截距不同,则答案加一
答案是: 40257
#include<bits/stdc++.h>
using namespace std;
const int N=2e5; // 最多的线数
int n;
struct Line{
double k,b; // 斜率和截距
bool operator<(const Line&t)const{
if(k!=t.k) return k<t.k;
return b<t.b;
}
}l[N];
int main(){
// 枚举所有点对
for(int x1=0;x1<20;x1++){
for(int y1=0;y1<21;y1++){
for(int x2=0;x2<20;x2++){
for(int y2=0;y2<21;y2++){
// 不枚举斜率不存在的点对,即x1=x2,最后加上20即可
if(x1!=x2){
double k=(double)(y2-y1)/(x2-x1);
double b=y1-k*x1;
l[n++]={k,b};
}
}
}
}
}
sort(l,l+n);
int res=1;
for(int i=1;i<n;i++){
// 如果相邻两个点对的斜率或者截距不同,则答案++
// 注意c++浮点数有误差判断浮点数相等时要注意
if(fabs(l[i].k-l[i-1].k)>1e-8||fabs(l[i].b-l[i-1].b)>1e-8){
res++;
}
}
cout<<res+20<<endl; // 40257
return 0;
}
试题D:货物摆放
思路:模拟
求出n的所有约数,然后暴力枚举a,b,c即可
答案是: 2430
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ll n=2021041820210418;
vector<ll> d; // 保存n的所有约数
for(ll i=1;i*i<=n;i++){
if(n%i==0){
d.push_back(i);
// 8/2=4!=2 说明4也是8的约数
if(n/i!=i) d.push_back(n/i);
}
}
int res=0;
// 枚举a,b,c
for(auto a:d)
for(auto b:d)
for(auto c:d)
if(a*b*c==n)
res++;
cout<<res<<endl; // 2430
return 0;
}
试题E:路径
思路:最短路
最短路算法都可以,这里我用的是spfa
答案是:10266837
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2200,M=N*50;
int n;
int h[N],e[M],w[M],ne[M],idx;
int q[N],dist[N];
bool st[N];
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
void add(int a,int b,int c){
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void spfa(){
queue<int> q;
memset(dist,0x3f,sizeof dist);
dist[1]=0;
q.push(1);
st[1]=true;
while(q.size()){
int t=q.front();q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(dist[j]>dist[t]+w[i]){
dist[j]=dist[t]+w[i];
if(!st[j]){
q.push(j);
st[j]=true;
}
}
}
}
}
int main(){
n=2021;
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(fabs(i-j)<=21){
int d=gcd(i,j);
add(i,j,i*j/d); // 最小公倍数 i*j/d
}
}
}
spfa();
cout<<dist[n]<<endl; // 10266837
return 0;
}
试题F:时间显示
小蓝要和朋友合作开发一个时间显示的网站。
在服务器上,朋友已经获取了当前的时间,用一个整数表示,值为从 1970 年 1 月 1 日 00:00:00 到当前时刻经过的毫秒数。
现在,小蓝要在客户端显示出这个时间。
小蓝不用显示出年月日,只需要显示出时分秒即可,毫秒也不用显示,直接舍去即可。
给定一个用整数表示的时间,请将这个时间对应的时分秒输出。
输入格式
输入一行包含一个整数,表示时间。
输出格式
输出时分秒表示的当前时间,格式形如 HH:MM:SS
,其中 HH
表示时,值为 0 到 23,M
表示分,值为 0 到 59,S
表示秒,值为 0 到 59。
时、分、秒不足两位时补前导 0。
数据范围
对于所有评测用例,给定的时间为不超过 10e18 的正整数。
输入样例1:
46800999
输出样例1:
13:00:00
输入样例2:
1618708103123
输出样例2:
01:08:23
思路:数学计算(c++) 日期类(Java)
C++写法:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ll n;
cin>>n;
n/=1000; // 去掉毫秒数
n%=(24*60*60); // 去掉整的天数,保留时分秒
int h=n/3600;
n%=3600; // 去掉小时
int m=n/60;
n%=60; // 去掉分钟
int s=n;
printf("%02d:%02d:%02d",h,m,s); // 02表示补零
return 0;
}
Java写法:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long n = sc.nextLong();
Date date = new Date(n);
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
System.out.println(sdf.format(date));
sc.close();
}
}
试题G:砝码称重
你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1,W2,⋅⋅⋅,WN。
请你计算一共可以称出多少种不同的正整数重量?
注意砝码可以放在天平两边。
输入格式
输入的第一行包含一个整数 N。
第二行包含 N 个整数:W1,W2,W3,⋅⋅⋅,WN。
输出格式
输出一个整数代表答案。
数据范围
对于 50% 的评测用例,1≤N≤15。
对于所有评测用例,1≤N≤100,N 个砝码总重不超过 105。
输入样例:
3
1 4 6
输出样例:
10
样例解释
能称出的 10 种重量是:1、2、3、4、5、6、7、9、10、11。
1 = 1;
2 = 6 − 4 (天平一边放 6,另一边放 4);
3 = 4 − 1;
4 = 4;
5 = 6 − 1;
6 = 6;
7 = 1 + 6;
9 = 4 + 6 − 1;
10 = 4 + 6;
11 = 1 + 4 + 6。
思路:背包(有限制的选择问题)O(n*m)
f[i][j]:表示只从前i个砝码中选,且总重量为j的所有方案的集合
求状态转义方程有一个简单的思想,就是 “看最后一步由什么推出来”
这里f[i][j]可以由前一步不选砝码,选砝码放在右边,选砝码放在左边这三种情况
1.不选砝码:f[i-1][j]
2.选砝码放在左边:f[i-1][j-w[i]] 选完w[i]之后 变成f[i][j] 这里w[i]为正
3.选砝码放在右边:f[i-1][j+w[i]] 选完w[i]之后 变成f[i][j] 这里w[i]为负
最后看一下f[n][1]~f[n][m] 有多少个非空即可
因为c++数组下标不能为负,所有要把所有的j加上一个偏移量m,这样j的范围为0~2m
C++写法:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=110,M=200010,B=M/2;//B是偏移量
int n,m;
int w[N];
bool f[N][M];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&w[i]),m+=w[i];
f[0][B]=true;
for(int i=1;i<=n;i++){
for(int j=-m;j<=m;j++){
// 有三种情况,如果三种情况中有一种情况为true,则
// f[i][j]为true 所有需要 | 运算 保证有一个为true即可
f[i][j+B]=f[i-1][j+B]; //不选砝码
if(j-w[i]>=-m) f[i][j+B]|=f[i-1][j-w[i]+B];//选砝码放在左边
if(j+w[i]<=m) f[i][j+B]|=f[i-1][j+w[i]+B];//选砝码放在右边
}
}
int res=0;
for(int j=1;j<=m;j++){
if(f[n][j+B]) res++;
}
cout<<res<<endl;
return 0;
}
Java写法: 略
试题H:杨辉三角形
下面的图形是著名的杨辉三角形:
如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:
1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, ...
给定一个正整数 N,请你输出数列中第一次出现 N 是在第几个数?
输入格式
输入一个整数 N。
输出格式
输出一个整数代表答案。
数据范围
对于 20% 的评测用例,1≤N≤10;
对于所有评测用例,1≤N≤10e9。
输入样例:
6
输出样例:
13
思路:二分 O(16logn) 找规律
显然,杨辉三角是对称的,每一个斜行都递增
同一行越靠下越大,所有要从最下方的斜行开始找,从内往外枚举
从第16个斜行开始枚举,如果第16个斜行中存在,则返回
否则看第15个斜行,每一个斜行都是单调递增的
因此可以二分一下,看一下每一个斜行是否存在目标值
C++写法:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int n;
LL C(int a, int b) {
LL res = 1;
for (int i = a, j = 1; j <= b; i --, j ++ ) {
res = res * i / j;
if (res > n) return res;
}
return res;
}
bool check(int k) {
LL l = k * 2, r = max((LL)n, l);
while (l < r) {
LL mid = l + r >> 1;
if (C(mid, k) >= n) r = mid;
else l = mid + 1;
}
if (C(r, k) != n) return false;
cout << r * (r + 1) / 2 + k + 1 << endl;
return true;
}
int main() {
cin >> n;
for (int k = 16; ; k -- )
if (check(k))
break;
return 0;
}
Java写法: 略
后面两题太难了。。。