选择题+编程题 官网有:GESP官网
这次的五级有点难啊, 比八级难…
编程题 1:小杨的幸运数
知识点: 筛法+后缀答案dp
思路:
首先筛法预处理出每个数是否是幸运数;
然后ans[i]表示i幸运化后的幸运数是多少:
那么如果i 是幸运数, ans[i] = i;
否则 i 不是幸运数, ans[i] = ans[i]+1
如果不后缀预处理答案 ans[i]数组, 每次往后查找可能会超时.
源代码
#include<bits/stdc++.h>
using namespace std;
int a,n,h[1100001],ans[1100001];
int t=1001*1001;
int main(){
cin>>a>>n;
for(int i=1;i*i<=t;++i)
if(i*i>=a)
for(int j=i*i;j<=t;j+=i*i)
h[j]=1;
for(int i=t;i>=1;--i)
if(h[i]) ans[i]=i;
else ans[i]=ans[i+1];
while(n--){
int x;scanf("%d",&x);
if(ans[x]==x)printf("lucky\n");
else printf("%d\n",ans[x]);
}
}
编程题 2:烹饪问题
知识点: 贪心+穷举, 贪心+位运算
思路1: 贪心+穷举
有同学直接穷举选的两个数, 这样时间复杂度O(n^2)肯定不行
所以他加个优化, 只穷举最大的前1000个数中的两个数, 时间复杂度O(1000^2)
比赛的时候他得了满分, 哈哈
源代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,a[N],mx=-0x7f7f7f7f;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a+1,a+n+1,greater<int>());
if(n>=1000){
for(int i=1;i<=1000;i++)
for(int j=i+1;j<=1000;j++)
mx=max(mx,a[i]&a[j]);
printf("%d",mx);
return 0;
}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
mx=max(mx,a[i]&a[j]);
printf("%d",mx);
}
思路2: 贪心+位运算
上面的方法肯定不行, 同学们想想怎么卡他的算法…
下面说思路, 因为是位与运算最大,
所以考虑二进制下的最高位(比如第30位)是否存在两个数都有1.
如果有那么最终答案一定包含这样的位, 接下来考虑第29位是否存在1(此时只能从第30位是1的数中考虑)
如果第30位为1的数没有2个或者以上, 那么是没有办法选出两个数位与后结果的30位为1,所以只需要重复上面的情况贪心即可.
时间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
源代码
#include<cstdio>
int n, a[1000009], ans;
void pd(int x){
int sum = 0;
for(int i=1;i<=n;++i)
if((a[i]>>x)&1) // a[i]&(1<<x)
{
sum++;
if(sum>=2) break;
}
if(sum>=2){
ans+= 1<<x; // 超过两个数存在, 答案一定包含2^x
int m=0; // 只保留第x位为1的数
for(int i=1;i<=n;++i)
if(a[i]&(1<<x))
a[++m] = a[i];
n = m;
}
}
int main(){
scanf("%d", &n);
for(int i=1;i<=n;++i)
scanf("%d", &a[i]);
for(int i=30;i>=0;i--)
pd(i);
printf("%d", ans);
}
/**************************************************************
Problem: 2284
User: admin [管理员]
Language: C++
Result: 正确
Time:300 ms
Memory:5100 kb
****************************************************************/
下面放一个更加简洁的写法, 少了数据拷贝的操作,反而更快!
#include<cstdio>
int n, a[1000009], ans;
void pd(int x){
int sum = 0;
for(int i=1;i<=n;++i)
if(((ans&a[i])==ans) && ((a[i]>>x)&1)) // a[i]&(1<<x)
sum++;
if(sum>=2) ans |= 1<<x;
}
int main(){
scanf("%d", &n);
for(int i=1;i<=n;++i)
scanf("%d", &a[i]);
for(int i=30;i>=0;i--)
pd(i);
printf("%d", ans);
}
/**************************************************************
Problem: 2284
User: Teacher0029 [陈老师]
Language: C++
Result: 正确
Time:194 ms
Memory:5100 kb
****************************************************************/