牛客小白月赛40
牛客小白月赛
A
这一题其实直接暴力就可以了。推荐一个函数:
__builtin_popcount(x) //返回x在二进制表示1的个数
__builtin_clz (unsigned int x) //返回前导的0的个数。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int T;
scanf("%d",&T);
while (T--) {
int x, ans = 0;
scanf("%d",&x);
while (x) {
if (__builtin_popcount(x) & 1)
x ^= 1;
else {
int bit = 31 - __builtin_clz(x);
x ^= 1 << bit;
}
ans++;
}
printf("%d\n", ans);
}
return 0;
}
D
签到题,判断有多少个两两相同连在一起的字符就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
ll n,m,k;
void solve(){
string s;
cin>>s;
int n=s.size();
int tt=n;
for(int i=0;i<tt-1;i++){
if(s[i]==s[i+1]) n++;
}
cout<<n<<endl;
}
int main(){
int T=1;
cin>>T;
while(T--) solve();
}
E
二分答案,二分最多的小组的人数
那么check函数,数据范围挺小的,直接可以桶 计数。判断最多可以分成多少组,组数是不是小于 m就可以了。
代码:
#include<bits/stdc++.h>
using namespace std;
int a[1000010],t[1000010];
int n,m;
bool chk(int mid){
if(mid==0) return true;//其实根本不会有这种情况
int ans=0,mx=0;
for(int i=1;i<=100000;i++){
if(t[i]){
ans=ans+(t[i]+mid-1)/mid;
}
}
return ans<=m;//判断能不能分那么多组
}
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>a[i];
t[a[i]]++;
}
int l=0,r=n;
int ans=100000;
while(l<=r){
int mid=(l+r)/2;
if(chk(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
if(ans==100000) cout<<-1<<endl;
else cout<<ans<<endl;
}
F
题意
有n个 方块,每一个方块都一个数 ,为正数可以往前跳,跳到 到 ,负数可以往回跳 可以跳到到 。
思路
这个题目有很明显的转移,且数据范围较小,可以采用动态规划。 假设 表示到达第 i 个方块的最短用时。由于负数相当于往回走,用时肯定更长,负数肯定可以直接跳过,有负数可能导致不能遍历到第 n 个 方块,如果可以遍历到第 n 个方块直接输出 ,如果不能输出 -1 ;
- 转移方程:
dp[i+a[i]]=min(dp[i]+1,dp[i+a[i]);
负数直接跳过
- code
#include<bits/stdc++.h>
using namespace std;
int dp[30000],a[20000];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],dp[i]=INT_MAX;
dp[1]=0;
for(int i=1;i<=n;i++){
if(a[i]>0){
for(int j=i+1;j<=i+a[i];j++){
dp[j]=min(dp[i]+1,dp[j]);
}
}
}
if(dp[n]==INT_MAX) cout<<-1<<endl;
else cout<<dp[n]<<endl;
}
G
差分、前缀和
枚举每一个K,去一个最大值
#include<bits/stdc++.h>
using namespace std;
int a[1000010];
int sum[6000010]={0};
int main(){
int n,m,p;
cin>>n>>p;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++){
sum[a[i]-p+1000000]++;
sum[a[i]+p+1000000+1]--;
}
int ans=0;
for(int i=0;i<=2000000;i++){
sum[i]+=sum[i-1];
ans=max(sum[i],ans);
}
cout<<ans<<endl;
}
H
要使得选的数的gcd等于x,那么选的数一定是 x的倍数,所以我们枚举 x,2x,3x,4x,如果在数组中出现过,我们就对他 取一个gcd,如果最后这些数的gcd等于 x,那么输出YES,否则输出NO
时间卡的有点紧,用快一点的输入输出
代码:
#include<bits/stdc++.h>
using namespace std;
int n,a[1000010],m,h[1000010];
void solve(){
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) h[i]=0;//注意不要用memset,会超时
for(int i=1;i<=n;i++) scanf("%d",&a[i]),h[a[i]]++;
int x;
while(m--){
scanf("%d",&x);
bool ok=true;
int p=x,tt=0;
for(int i=x;i<=n&&tt!=x;i+=x){
if(h[i]) tt=__gcd(i,tt);
}
if(tt==x) printf("YES\n");
else printf("NO\n");
}
}
int main(){
int t;
cin>>t;
while(t--) solve();
}
I
范围只有到10,直接STL,全排列函数,next_permutation(a.begin(),a.end()),返回下一个比他大的全排列。判断每一个排列是否符合条件。
DFS搜全排列也是一样的。
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define endl '\n'
typedef long long ll;
const int N=5e5+10;
const int M=1e6+7;
const int mod=1e9+7;
ll n,m,k;
ll a[N],b[N];
void solve(){
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++) b[i]=i+1;
int ans=0;
do{
bool ok=true;
for(int i=0;i<n;i++){
int pos=0;
if(a[i]==i+1) continue;
for(int j=0;j<n;j++){//找到他的位置,
if(b[j]==i+1) {
pos=j+1;
break;
}
}
for(int j=0;j<pos;j++){//判断是不是在他的前面
if(b[j]==a[i]){
ok=false;
break;
}
}
}
if(ok) ans++;
}while(next_permutation(b, b+n));
cout<<ans<<endl;
}
int main(){
solve();
}