目录
试题A:空间
1MB等于1024KB,1KB=1024B,B=byte 字节,1B=8bits,bits是位数,所以32位二进制整数就占4个字节,
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int main()
{
cout<<256*1024*1024*/4<<endl;
return 0;
}
最后答案为: 67108864.
试题B:卡片
这题直接模拟就好,用一个数组来存0~9的个数,然后从1开始枚举,每个数分别把每个位拿出来判断,当判断到当前数字不够用的时候就判false,然后枚举到第i个不能成立时,输出i-1即为答案。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int st[10];
bool check(int x)
{
while(x)
{
int t=x%10;
x/=10;
if(--st[t]<0)return false;
}
return true;
}
int main()
{
for(int i=0;i<10;i++)st[i]=2021;
for(int i=1; ;i++)
if(!check(i))
{
cout<<i-1<<endl;
return 0;
}
}
答案:3181
试题C:直线
枚举每一条直线,用结构体来存每一条边的斜率和截距,然后斜率小的排在前,斜率相同则截距小的排在前,把每一条边按从小到大排序好,遍历每一条直线, 然后判断斜率不同的有多少,fabs为用来求浮点数的绝对值,浮点数的存储在计算机内是有误差的,判断两个double数是否相等,只要这两个数的差在1e-8以内就算相等。最后还要记得加上斜率为0的20条竖线。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N =200000;
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++)
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++)
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;
return 0;
}
答案:40257
试题D:货物摆放
这题可以先去求n的所有约数,然后三重循环枚举一下哪些相乘等于n的。
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
typedef long long LL;
int main()
{
LL n;
cin>>n;
vector<LL>d;
for(LL i=1;i*i<=n;i++)
if(n%i==0)
{
d.push_back(i);
if(n/i!=i)d.push_back(n/i);
}
int res=0;
for(auto a:d)
for(auto b:d)
for(auto c:d)
if(a*b*c==n)
res++;
cout<<res<<endl;
return 0;
}
试题E:路径
,那么每个点最多有 21*2=42 条边,2021个点*42条边那么这题就是个很经典的最短路问题,这里我使用spfa算法解决
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
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()
{
int hh=0,tt=0;
memset(dist,0x3f,sizeof dist);
dist[1]=0;
q[tt++]=1;
st[1]=true;
while(hh!=tt)
{
int t=q[hh++];
if(hh==N)hh=0;
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[tt++]=j;
if(tt==N)tt=0;
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++)
{
int d=gcd(i,j);//求出最大公约数
if(i!=j)
{
if(fabs(i-j)<=21)
{
add(i,j,i*j/d); 两个数相乘除以最大公约数就可以求出最小公倍数
}
}
}
spfa();
printf("%d\n",dist[n]);
return 0;
}
F 时间显示
因为不用考虑年月日,只需要考虑当天的时间即可
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int main()
{
LL n;
cin>>n;
n/=1000;//因为不用考虑毫秒就把毫秒除掉
n%=86400;//一天是86400秒,因为只求当天的时间把今天之前的时间除了,取余就是今天的秒数
int h=n/3600;//小时数
n%=3600;//除去小时后剩下的时间
int m=n/60;//除以60便是分钟
int s=n%60;//取余便是除去小时很分钟后剩下的时间
printf("%02d:%02d:%02d\n",h,m,s);
return 0;
}
G 砝码称重
背包问题,dp的状态可以分为,不选wi 和选+wi(即放在天平左边),选-wi(即放在天平右边)
#include <bits/stdc++.h>
using namespace std;
const int N = 110, M = 2e5 + 10;
int sum;
int n;
int w[N];
bool f[N][M];
int main() {
cin>>n;
for (int i = 1; i <= n; i++)
{
scanf("%d", &w[i]);
sum+=w[i];
}
f[0][0]=true;
for (int i = 1; i <= n;i++)
for (int j = 0; j <=sum;j++)
f[i][j]=f[i-1][j]||f[i-1][j+w[i]]||f[i-1][abs(j-w[i])];
//只要有一个非空,f[i][j]就非空,使用abs避免了数组下标为负数的情况
int ans = 0;
for (int i = 1; i <=sum;i++)
if(f[n][i])ans++;//不为零说明可以选出这个质量的砝码
cout << ans;
return 0;
}
H 杨辉三角形
本题是思维题,考的是数学+二分查找,杨辉三角是左右对称的,所以我们只用看一半即可
斜着看每一条斜线我们可以从图中发现除了第一行外,每一行都是单调递增的,又因为杨辉三角就是组合数,所以每一行的最小值可以表示C(k 2k) k 是当前行(横着)第几个数如图所示
有图我们可以得出以下性质:
1. 每一斜行从上到下递增
2. 每一横行从中间到两边依次递减
n最大1e9,C(34, 17) > 1e9, C(32, 16) < 1e9,因此只要枚举前16个斜行即可!
因为是斜着的每一行是单调递增的,所以我们可以用二分来找到目标值 nn, 又因为每一斜行也是单调递增的,所以我们从最里面开始往外找
上边界 l=2k
下边界 r=n (如果内层斜行都没找到,一定出现在第2行,因为Cn1=n)
所以我们可以枚举每一斜行的开头元素(最小元素)k, 从 16 开始枚举
二分的check就是 如果 Ckmid>=n,取左区间,否则取右区间
当我们找到了这个数的时候即C(r,k)==n (r为底数)
我们通过找规律发现 r 为前面共多少行(不算这一行),第一行有 1 个数,第二行有 2 个数,第三行有 3个数,第 n 行有 n 个数,也就是前面一共有 r(1+r)2 个数, k 为这一行第几个(下标都从 0 开始),所以这个数是第 r(1+r)/2 +k+1 个数
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int n;
// C(a, b) = a!/b!(a-b)! = a * (a-1) .. b个 / b!
LL C(int a, int b){
LL res = 1;
for(int i = a, j = 1; j <= b; i --, j ++){
res = res * i / j;
// 大于n已无意义,且防止爆LL
if(res > n) return res;
}
return res;
}
bool check(int k){
// 二分该斜行,找到大于等于该值的第一个数
// 左边界2k,右边界为max(l, n)取二者最大即可!
int l = 2 * k, r = max(n, l);
while(l < r){
int mid = l + r >> 1;
if(C(mid, k) >= n) r = mid;
else l = mid + 1;
}
if(C(r, k) != n) return false;
// C(r, k)的从0开始的顺序!
cout << (r + 1) * r / 2 + k + 1 << endl;
return true;
}
int main(){
cin >> n;
// 从第16斜行枚举即可!
for(int k = 16; ; k --)
if(check(k)) break;
return 0;
}