蓝桥杯刷题记录:2021第十二届C++省赛真题(待更新)

T1:砝码称重

原题链接:https://www.dotcpp.com/oj/problem2604.html

DP代码:(100分)

#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=110,M=2e5+10;
int sum,n,w[N];
bool f[N][M];

int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;++i)
	{
		cin>>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])];
		}
	}
	int ans=0;
	for(int i=1;i<=sum;i++)
		if(f[n][i])
			ans++;
	cout<<ans<<endl;
	return 0;
} 

DFS代码:(100分)

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int n,res;
int w[1000000];
bool st[1000000];
bool memo[110][200010];//记忆化搜索需要 
void dfs(int k,int sum)//考虑k个砝码重量凑出sum,中间有跳过不选的环节 
{//不一定是全部都选上 
	if(memo[k][sum])//如果sum被凑出过直接返回bool判断值 
		return; 
	memo[k][sum]=true;
	st[sum]=true;
	if(k>n)//全部选择完了
	{
		return;
	}
	else//考虑全部情况,考虑过就行 
	{
		dfs(k+1,abs(sum-w[k]));//放右边 
		dfs(k+1,sum);//不选 
		dfs(k+1,sum+w[k]);//放左边 
	}
}
int main()
{
	cin>>n;
	int ans=0;
	for(int i=1;i<=n;i++)
		cin>>w[i];
	dfs(1,0);
	for(int i=1;i<=200010;i++)
		if(st[i])
			ans++;
	cout<<ans<<endl;
	return 0;
}

T2:异或序列:

题解:

    初始时A和B都为0, 由异或的性质最终 A⨁B=X1⨁X2⨁Xi…
(1)如果所有XX异或的结果为0,那么说明最终的A 和 B 是相同的,直接输出平局00。
(2)如果要使得最终结果最大,那么肯定优先选最高位的1,我们记录每一位上1出现的次数
由于异或中:
与 1 异或是取反,与 0 异或不变,

也就是把所有数的每一位的1统计个数,统计结果为所有数在所有位上出现的个数。
当某一位的num[i]为偶数,则游戏结果的这一位一定相等(取反偶数次还是原数),直接看下一位出现的次数

如果num[i]的个数为1,则一定是先手赢(抢了1之后,就没有1了|只有A最高位变成了1)(可以理解为没有1让他取反了
那么当num[i]为大于1的奇数, 0的个数为偶数,则先手赢——
先手抢1,然后之后都和对手取一样
当num[i]为大于1的奇数, 0的个数为奇数,则后手赢——先手抢1,0都反他

时间超限83%:

#include<stdio.h>
#include<iostream> 
#include<algorithm>
#include<cstring>
using namespace std;
const int N=22;
int num[N];//总的每位1出现个数 
int k,sum,n;
void jilu(int n)
{
	int cnt=1;
	while(n)//位运算 
	{
		if(n&1)//判断这一位是不是1 
			num[cnt]++;
		cnt++;
		n>>=1;//整体左移,把最后一位搞掉,反正后面也用不到了 
	}
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		memset(num,0,sizeof num);
		sum=0;
		cin>>k;
		for(int i=0;i<k;i++)
		{
			int s;
			cin>>s;
			jilu(s);//处理下num[] 
			sum=sum^s;//反正终点都是sum=x[1]^x[2]^……^x[n] 
		}
		if(!sum)//如果结果不是1的话直接平局 
			puts("0"); 
		else
		{
			for(int i=20;i>=1;i--)//最大数不会超2^20 
			{
				if(num[i]==1)//只有一个1先手抢就行 
				{
					puts("1");
					break;
				}
				else if(num[i]&1)//判断1个数是奇数 
				{
					if(k%2==0)//0奇数后手赢 
					{
						puts("-1");
						break;
					} 
					else
					{
						puts("1");
						break;
					}
				} 
			}
		} 
	} 
	return 0;
}

T3:左孩子右兄弟:

vector建树:

//vector建树法 
#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
#define N 100010
using namespace std;
vector<int > g[N];//存子节点 
int n,x; 
int dfs(int u)
{
	int ans=0;
	int cnt=g[u].size();//记录根节点的孩子数,最后加上就行 
	for(int i=0;i<g[u].size();i++)//开始找最深子树的结点数 
	{
		ans=max(ans,dfs(g[u][i])+cnt); 
	} 
	return ans;
}
int main()
{
	cin>>n;
	for(int i=2;i<=n;i++)
	{
		cin>>x;//输入他的父节点 
		g[x].push_back(i);//vector存父节点下属的子节点是哪一个 
	} 
	int sum=dfs(1);
	cout<<sum<<endl;
	return 0;
}
/*想错了哈哈哈int n,m,sum=0;
int a[N],maxx=0;

int main()
{
	cin>>n;
	for(int i=1;i<n;i++)
	{
		cin>>a[i];
		maxx=max(maxx,a[i]);
	}
	a[1]=1;
	sum=1;
	while(a[sum]!=maxx)
	{
		sum++;
	}
	cout<<sum<<endl;
	return 0;
 }*/ 

邻接表建树法:

#include<stdio.h>
#include<iostream>
#include<cstring> 
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N],ne[N],e[N],h[N],idx;
int num[N],f[N];
void add(int a,int b)//邻接表建树 
{
	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;
}
int dfs(int u,int fa)
{
	for(int i=h[u];!i;i=ne[i])
	{
		int j=e[i];
		if(j!=fa)//保证递归向下 
		{
			dfs(j,u);
			f[u]=max(f[u],f[j]+num[u]); 
		}
	}
} 
int main()
{
	int n;
	cin>>n;
	memset(h,-1,sizeof h);
	for(int i=2;i<=n;i++)
	{
		int x;
		cin>>x;
		add(x,i);
		num[x]++;//子节点个数 
	}
	dfs(1,-1);
	cout<<f[1]<<endl;//以1为根的树高度 
	return 0;
}

T4杨辉三角形:

思路图:

 代码详解:

#include<stdio.h>
#include<iostream>
#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;j++,i--)
	{
		res=res*i/j;
		if(res>n)//大于n的话不用管 
			return res;
	}
	return res; 
}
bool check(int k) 
{
	int l=2*k,r=max(n,l);//C(2k,k)-->C(n,k) 这里取n的原因我查到的就是以n为底的组合数必然比n大,但是可能会爆呀,搞不懂 
	//里取n的原因我查到的就是以n为底的组合数必然比n大
	//反正在后面取组合数的函数里大于后会被直接返回不用管的 
	
	while(l<r)//划分成[l,mid][mid+1][r],模板1 
	{
		LL mid=l+r>>1; 
		if(c(mid,k)>=n)
			r=mid;//右边界取 
		else
			l=mid+1;
	}
	if(c(r,k)!=n)
		return false;
	cout<<1ll*(r+1)*r/2+k+1<<endl;//输出序列号 
	return true; 
}
 
int main()
{
	cin>>n;
	for(int k=16;;k--)//最大斜行数量不会超过16行 C(32,16)>1e9 
	{
		if(check(k))//找到当前斜行存在值就退出 
			break; 
	}
	return 0;
}

T5:最少砝码:

#include <iostream>
#include <cmath>
using namespace std;
int main() {
    int k;
    cin >> k;
    cout << ceil(log(2*k+1)/log(3)) << endl;
}

T6:特殊年份(简单题)

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
 
int n,m,k;
int a,b,c,d;
char f[5];
int main()
{
	int cnt=0;
	for(int i=1;i<=5;i++)
	{
		for(int j=1;j<=4;j++)
		{
			cin>>f[j];
			if(j==1)
				a=f[j]-'0';
			if(j==2)
				b=f[j]-'0';
			if(j==3)
				c=f[j]-'0';
			if(j==4)
				d=f[j]-'0';
		}
		if(a==c && d==b+1)
			cnt++;
	}
	cout<<cnt<<endl;
	return 0;
 } 

T7:小平方(简单题)

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n,k;
double m;
int cnt=0;
int main()
{
	cin>>n;
	m=n/2.0;
	for(int i=1;i<n;i++)
	{
		if((i*i)%n<m)
			cnt++;
	}
	cout<<cnt<<endl;
	return 0;
} 

T8:完全平方数(简单题)

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
typedef long long ll;
#define N 1000010
using namespace std;
ll n,m;
int main()
{
	cin>>n;
	ll res=1;
	for(ll i=2;i*i<=n;i++)//找质因子
	{
		if(n%i==0)//看他的这个质因子有几个 
		{
			int s=0;
			while(n%i==0)
			{
				s++;
				n/=i;
			} 
			if(s%2)//如果是奇数答案就要乘上他成为一个偶数 
				res*=i;
		}
	} 
	if(n>1)//还剩下单个的,凑个双 
		res*=n; 
	cout<<res<<endl; 
	return 0;
} 

T9:负载均衡(优先队列模拟)

感谢Mangata大佬的博客指点~~~

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
#define ll long long
#define N 200010
int n,m,qq,a,b,c,d,v[N];
priority_queue<PII,vector<PII>,greater<PII> >q[N]; 
int main()//用优先队列原因:我们需要找到最先结束的任务时间以及精力 
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)	cin>>v[i];
	for(int i=0;i<m;i++)
	{
		cin>>a>>b>>c>>d;
		while(q[b].size())
		{
			int ed=q[b].top().first;//最开始结束的任务时间 
			int k=q[b].top().second;//当前最先结束的任务精力
			if(ed>a)//如果现在的最先结束任务的时间已经超出那么后面任务就不用考虑了 
				break;
			v[b]=v[b]+k;//任务结束后就还原精力,给下一步使用
			q[b].pop();//把目前结束的任务弹出 
		} 
		if(v[b]<d)
		{
			cout<<"-1"<<endl;//剩余精力不足以完成现在的任务 
		} 
		else//把任务放入队列中,留着慢慢处理 
		{
			q[b].push({a+c,d});//他现在的结束的时间就是起始时间+耗费时间
			v[b]=v[b]-d;
			cout<<v[b]<<endl; 
		}
	}
	return 0;
}

T10:填空题卡片:

 

#include<bits/stdc++.h>
using namespace std;

int n,m,k;
int a[10]; 
int main()
{
	for(int i=0;i<=9;i++)
		a[i]=2021;
	int n=1;
	while(1)
	{
		int s=n;
		while(s){
			if(a[s%10])
				a[s%10]--;
			else
				break;
			s/=10;
		}
		if(s)
			break;
		else
			n++;
	}
	cout<<n-1<<endl;
	return 0;
}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值