T1_第几天
——题目描述——
——解——
口算题,
31+29+31+30+4=125
最终答案:125
T2_明码
——题目描述——
——解——
本题考察内容为进制转化
测试数据中出现-128,暗示用8位补码表示负数。
补码表示方法:先取绝对值当做正数写出求出原码,然后在末尾加1,最后做一次进位操作。
具体是哪十个汉字,就不剧透了。
最终答案:387420489
——Code——
#include<iostream>
using namespace std;
int check[8]={128,64,32,16,8,4,2,1};
int ans[8];
int main()
{
for(int i=1;i<=10;++i)
{
for(int j=1;j<=32;++j)
{
int num;
bool flag=false;
cin>>num;
if(j&1) cout<<endl;
if(num<0)
{
num=-num;
flag=true;
}
for(int k=0;k<8;++k)
{
if(num>=check[k])
{
ans[k]=1;
num-=check[k];
}
else ans[k]=0;
}
if(flag)
{
int p;
for(p=0;p<8;++p)
ans[p]=1-ans[p];
--p;
ans[p]+=1;
while(ans[p]==2&&p>=1)
{
ans[p]=0;
ans[p-1]+=1;
--p;
}
}
for(int k=0;k<8;++k)
{
if(ans[k]) cout<<ans[k];
else cout<<" ";
}
}
cout<<endl;
}
return 0;
}
T3_乘积尾零
——题目描述——
——解——
本题是一个很简单的数学题。
我们知道,10只能有2乘5获取,抓住这一点,只要把每一个数分解乘2的几次方乘5的几次方以及一个无关数字。并且2和5是互质的,不会出现包含彼此,重复计算的情况。最后,一个2和一个5配对生成一个尾数0,所以最后只要输出2和5最小的指数即可。
最终答案:31
——Code——
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int num[100]={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};
int main()
{
int cnt_2=0,cnt_5=0;
for(int i=0;i<100;++i)
{
while(!(num[i]%2))
{
num[i]/=2;
++cnt_2;
}
while(!(num[i]%5))
{
num[i]/=5;
++cnt_5;
}
}
cout<<min(cnt_2,cnt_5)<<endl;
return 0;
}
T4_测试次数
——题目描述——
——解——
本题是本次比赛一个算有点东西的题目,首先想到的是二分法,但是手机摔坏了就没了,在手机耐摔指数极低的情况下大概率三部手机全坏也得不出具体结果。因此,本题不应该采用二分法。
本题中的关键词“运气最坏”可以解释为:测试n次,能确定一个一定找出答案的楼层上限。
比如,只有一部手机,那么测试n次,最多能覆盖到楼层n。
如果有两部手机,可以现在第n层测试,如果坏了,那么用余下的一部手机在1到n-1层测试;如果没坏,可以在n+n-1层测试,同理如果这次坏了,则考察n+1到n-2,否则继续递推n+n-1+n-2层。直到用完这n次测试机会,最高可以测试到n(n+1)/2层,这样做也符合题目中所说的“最佳策略”。
然后,现在我们手中有三部手机, 可以参照两部手机的做法,还是只有n次测试机会,先随便找一层扔一下,发现手机坏了,那么就退化成两部手机的情况,可以测试到楼层n(n-1)/2。因此,可以发现,第一次扔手机的最佳楼层为n(n-1)/2+1,也即三部手机n(n-1)/2+1层的测试数=两部手机n(n-1)/2层的测试数+1。如果手机没坏,那么继续测试n(n-1)/2+1+(n-1)(n-2)/2+1层的情况。依次类推,我们得出三个简单的递推式。
m为层数,k模拟每次选定的中途测试点
设F1为一部手机的情况:
;
设F2为两部手机的情况:
设F3为三部手机的情况:
最终答案:19
——Code——
#include<iostream>
using namespace std;
const int M=1000;
int F1[M+1],F2[M+1],F3[M+1];
int main()
{
for(int m=1;m<=1000;++m)
F1[m]=F2[m]=F3[m]=m;
for(int m=1;m<=1000;++m)
for(int k=2;k<=m-1;++k)
F2[m]=min(F2[m],max(F2[m-k],F1[k-1])+1);
for(int m=1;m<=1000;++m)
for(int k=2;k<=m-1;++k)
F3[m]=min(F3[m],max(F3[m-k],F2[k-1])+1);
cout<<F3[1000]<<endl;
return 0;
}
T5_快速排序
——题目描述——
#include <stdio.h>
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;
}
——解——
这题相比上一题明显水了很多,本题考察递归中的分治法。
首先,随机取数组中的一个数
作为基准,大于该数的排在它右边,其余排在左边。然后在分别对这两个区间进行之前的操作,题目只要求找出第k小的数字,所以当基准数前面的数字大于k时,就对基准数前面的数字进行排序;如果小于k,为i个,则对后面的数排序,且找后面第k-(i-l+1)小的数。
最终答案:a, j+1, r, k-i+l-1
T6_递增三元组
——题目描述——
——解——
难度仍旧不及T4,本题数据高达1e5,如果强行枚举达到1e15,绝对是不可能跑完的。
正确的思路是利用数列的单调性求解。首先对三个数列进行从小到大排序(快排),从数列B开始遍历。当j=1时,找到一个恰好比B1小的Ai,然后在找一个恰好比B1大的Ck,能够组成的个数加上Ai的个数i和Ck的个数n-k+1,依次类推。
由于缺少数据,接下来几题的代码都只经过样例测试。
——Code——
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[100005],b[100005],c[100005];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=n;++i) cin>>b[i];
for(int i=1;i<=n;++i) cin>>c[i];
sort(a+1,a+n+1);
sort(b+1,b+n+1);
sort(c+1,c+n+1);
long long ans=0;
for(int j=1;j<=n;++j)
{
int i=1,k=1;
while(i<=n&&b[j]>a[i])
++i;
while(k<=n&&c[k]<=b[j])
++k;
ans+=(i-1)*(n-k+1);
}
cout<<ans<<endl;
return 0;
}
T7_螺旋折线
——解——
本题是找规律题,我的参照坐标为第三象限在直线y=x上的点,该点处的值为 (-2x+1)2-1,然后推出其他点的值。
——Code——
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
long long ans=0;
long long x,y;
cin>>x>>y;
long long c=max(max(x,-x),max(y,-y));
long long base=(2*c+1)*(2*c+1)-1;
if(y==-c) ans=base-(x+c);
else if(x==c) ans=base-2*c-(y+c);
else if(y==c) ans=base-4*c-(c-x);
else if(x==-c) ans=base-6*c-(c-y);
cout<<ans<<endl;
return 0;
}
T8_日志统计
——题目描述——
——解——
本题依旧是水题,主要考察尺取法,它可以用于检查一段连续区间是否满足某一性质。
我们首先把不同id的获赞情况分别记录,比如某帖子,分别在t1,t2,t3,t4…tn获赞。在进行尺取法之前,先设区间头i和尾j都在0的位置,此时获赞数为1,我们发现获赞数不足K,所以尾j向后移动直到获赞数为K,退出j向后延伸的循环。然后检查距离tj-ti,如果小于D,则认为符合“热帖”规定,否则i向后移直到距离小于D。循环从检查获赞数开始的操作,直到i和j都到达tn时刻。
——Code——
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
vector<int> re[100005];
int check[100005];
int main()
{
int N,D,K;
cin>>N>>D>>K;
for(int i=1;i<=N;++i)
{
int ts,id;
cin>>ts>>id;
re[id].push_back(ts);
}
for(int k=1;k<=100000;++k)
{
if(!re[k].size()) continue;
int len=re[k].size();
for(int i=0;i<len;++i)
check[i]=re[k][i];
sort(check,check+len);
int i=0,j=0,dis=0;
int sum=1;
while(sum<k&&j<len)
{
++j;
++sum;
}
dis=check[j]-check[i];
while(dis>D&&i<j)
{
++i;
dis=check[j]-check[i];
}
if(sum==k&&dis<D)
{
cout<<k<<endl;
continue;
}
}
return 0;
}
T9_全球变暖
——题目描述——
——解——
本题跑一遍BFS就可以了,注意找四周没有海的区块,如果某个岛遍历完仍然没有找到这样的区块,则答案数加1
——Code——
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
char mp[1003][1003];
bool vis[1003][1003];
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
struct CO{
int x;
int y;
};
queue<CO> q;
int n;
int ans;
void bfs(int nx,int ny)
{
CO node;
node.x=nx;
node.y=ny;
q.push(node);
vis[nx][ny]=true;
bool flag=false;
while(!q.empty())
{
node=q.front();
q.pop();
int cnt=0;
bool flag1=false;
for(int i=0;i<4;++i)
{
int X=node.x+dx[i];
int Y=node.y+dy[i];
if(X>=0&&X<n&&Y>=0&&Y<n)
{
if(mp[X][Y]=='#'&&vis[X][Y]==false)
{
CO newnode;
newnode.x=X;
newnode.y=Y;
vis[X][Y]=true;
q.push(newnode);
}
else if(mp[X][Y]=='.') flag1=true;
}
}
if(!flag1) flag=true;
}
if(!flag) ++ans;
}
int main()
{
cin>>n;
for(int i=0;i<n;++i)
scanf("%s",mp[i]);
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
if(mp[i][j]=='#'&&vis[i][j]==false)
bfs(i,j);
cout<<ans<<endl;
return 0;
}
T10_最大乘积
——题目描述——
——解——
思路简单,但是坑点较多。
分为三大种情况:
①只有负数和0,a.若k为奇数,选绝对值小的数,b.若k为偶数,选绝对值大的数;
②只有正数和0,选择绝对值大的前几个;
③正负数和0都有,按绝对值大小,进行排序,从最大的开始选择,负数尽量选偶数个,余下的取正数。
——Code——
#include<iostream>
#include<algorithm>
#define mod 1000000009
using namespace std;
int main()
{
cout<<"gugugu"<<endl;
return 0;
}
代码咕了