2018年的蓝桥杯也快开始了,去年我没有报。今年打算参加一次。本来报的A组,
但是评估了一下自己的实力。去A组可能就是个炮灰。因为鄙人是电子专业的,开始
做算法题大概一个月多点,中间夹杂着过年的放纵,开学的复习。现在总结一下2016蓝
桥杯的题。用的时间可能有4个半小时吧。题基本上都是自己做的,除了第九题。
第二个是分子除以分母不好处理,要想到都变换一下,两边都乘两个分母(C,GHI)。
直接用next_permutation()函数进行枚举,然后判断是否可以。如果两个点有一个卡住了,那么就
,但是根据bfs的思路好像也会重复,并且感觉找到一种以后也不好退回。
那只能老老实实的枚举了。枚举以后用dfs判断。但是这道题如果真的是考试中,那我肯定做不出来,因为写的过程中总
是又疏忽的地方。看了答案再检查才发现错误。改完以后我就忘记是怎么错的了,现在想复现一下都忘了。以后一定要直
时间复杂度更低的方法。先将两个数字的和的情况全部枚举出来,放进一个数组,然后排序。最后两重循环二分查找是否再
除去。最后剩下直接的k个,k-1次就可以互换完全。当时脑子一片混乱,就没想出来,害怕复杂度太高会超时,因为查的时候要两重循环。然后想想与其这样查,不如直接两重循环每回都把当前位置需要的换回来,发现也是ok的。但是估计真到考试这道题也是得不满的。这道题我花了许久时间。真到考试,可能第十题都没时间看了。
觉得会和会是两码事,多练是真的。
写博客呢,是想记录一下,要不然都得忘了。
感觉写博客会让人进步= = 但是我懒...
但是评估了一下自己的实力。去A组可能就是个炮灰。因为鄙人是电子专业的,开始
做算法题大概一个月多点,中间夹杂着过年的放纵,开学的复习。现在总结一下2016蓝
桥杯的题。用的时间可能有4个半小时吧。题基本上都是自己做的,除了第九题。
题目我就不去复制了,只写解法。
(代码中的红色是因为我直接点了html/xml的格式)
第一题 煤球数目
送分题。找规律,1,1+2,1+2+3...开个数组,记录每层的,一个总的加上。得结果171700
#include <iostream>
using namespace std;
int dp[102];
int main()
{
int i;
dp[0]=0;
int sum=0;
for(i=1;i<=100;i++){
dp[i]=dp[i-1]+i;
sum+=dp[i];
}
cout<<sum<<endl;
return 0;
}
第二题 生日蜡烛
送分题。穷举,从一岁开始,每次都一直加,知道超过236,等于的话就推出得到结果。
#include <iostream>
using namespace std;
int dp[300];
int main()
{
int i;
for(i=1;i<=236;i++){
int j=i,sum=0;
int flag=0;
while(1){
sum+=j++;
if(sum==236){flag=1;break;}
if(sum>236)break;
}
if(flag)break;
}
cout << i << endl;
return 0;
}
第三题 凑算式
这题就不是送分题了。因为有两个点,一个是要想到这里面的分子式有可能两个加起来是整数。第二个是分子除以分母不好处理,要想到都变换一下,两边都乘两个分母(C,GHI)。
直接用next_permutation()函数进行枚举,然后判断是否可以。如果两个点有一个卡住了,那么就
很可能算出错误答案。
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int a[20];
void permutation(){
int s1,s=0;
int s2,s3,m1,m2;
do{
m1=a[2];//分母C
m2=(100*a[6]+10*a[7]+a[8]);//分母GHI
s1=a[0]*m1*m2;//s1,s2,s3是左边的三项
s2=a[1]*m2;
s3=(100*a[3]+10*a[4]+a[5])*m1;
if(s1+s2+s3==10*m1*m2){
s++;
}
}while(next_permutation(a,a+9));
cout<<s<<endl;
}
int main()
{
int i;
for(i=0;i<9;i++){
a[i]=i+1;
}
permutation();
return 0;
}
第四题 快速排序
送分题 虽然我没学过快排,但是题目中已经说清楚了。看懂代码,然后直接就能得出答案。
swap(a,p,j);
第五题 抽签
这个题是卡住我许久的题。原因是错误的把画横线的部分当作j循环下的了。首先肯定要填f(x,x,x,x),其中第一个和第四个肯定不变。k代表字母,用完了肯定+1,然后m是选了多少个,通过分析代码,是选了i个a[k],所以传递的就是m-i。f(a,k+1,m-i,b);
第六题方格填数
两种方法,一种是直接dfs,一个数一个数填,但是会很慢,而且容易出错。第二种还是用next_permutation函数,枚举所有的可能,判断一下就行。会写就很容易,还不容易错。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int a[10];
int sum=0;
bool if_can(){
if(abs(a[0]-a[1])==1||abs(a[1]-a[2])==1)return false;
if(abs(a[3]-a[4])==1||abs(a[4]-a[5])==1)return false;
if(abs(a[5]-a[6])==1||abs(a[7]-a[8])==1)return false;
if(abs(a[8]-a[9])==1||abs(a[0]-a[3])==1)return false;
if(abs(a[0]-a[4])==1||abs(a[0]-a[5])==1)return false;
if(abs(a[1]-a[4])==1||abs(a[1]-a[5])==1)return false;
if(abs(a[1]-a[6])==1||abs(a[2]-a[5])==1)return false;
if(abs(a[2]-a[6])==1||abs(a[3]-a[7])==1)return false;
if(abs(a[3]-a[8])==1||abs(a[4]-a[7])==1)return false;
if(abs(a[4]-a[8])==1||abs(a[4]-a[9])==1)return false;
if(abs(a[5]-a[8])==1||abs(a[5]-a[9])==1)return false;
if(abs(a[6]-a[9])==1)return false;
return true;
}
void permutation(){
do{
if(if_can()){
sum++;
}
}
while(next_permutation(a,a+10));
}
int main(){
for(int i=0;i<10;i++){
a[i]=i;
}
permutation();
cout<<sum<<endl;
}
第七题 剪邮票
这道题也花了不少时间去想,想直接DFS吧,又怕重复,并且dfs没法得到支路的图形,所以不行。Bfs吧,我还不太熟悉,但是根据bfs的思路好像也会重复,并且感觉找到一种以后也不好退回。
那只能老老实实的枚举了。枚举以后用dfs判断。但是这道题如果真的是考试中,那我肯定做不出来,因为写的过程中总
是又疏忽的地方。看了答案再检查才发现错误。改完以后我就忘记是怎么错的了,现在想复现一下都忘了。以后一定要直
接记下来。
#include <iostream>
#include <string.h>
#include <cstdio>
using namespace std;
int d[4][5];
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
int used[4][5];
int sum=0;
int f;
int cnt;
void dfs(int x,int y){
int i,nx,ny;
if(cnt==5){
f=1;
sum++;
return;
}
for(i=0;i<4;i++){
nx=x+dx[i];
ny=y+dy[i];
if(nx>=1&&nx<=3&&ny>=1&&ny<=4&&d[nx][ny]==1&&!used[nx][ny]){
used[nx][ny]=1;
cnt++;
dfs(nx,ny);
//used[nx][ny]=0;
}
if(f)return;
}
}
int main()
{
int i,j,k,l,m;
for(int i=1; i<=8; i++)
{
for(int j=i+1; j<=9; j++)
{
for(int k=j+1; k<=10; k++)
{
for(int l=k+1; l<=11; l++)
{
for(int m=l+1; m<=12; m++)
{
memset(d,0,sizeof(d));
memset(used,0,sizeof(used));
f=0;
d[(i-1)/4+1][(i-1)%4+1]=1;//将数变为了二维的放进了数组,然后判断。
d[(j-1)/4+1][(j-1)%4+1]=1;//有点麻烦,我看有的是直接一维去判断。
d[(k-1)/4+1][(k-1)%4+1]=1;
d[(l-1)/4+1][(l-1)%4+1]=1;
d[(m-1)/4+1][(m-1)%4+1]=1;
for(int ii=1;ii<=3;ii++){
for(int jj=1;jj<=4;jj++){
printf("d[%d][%d]=%d ",ii,jj,d[ii][jj]);
}
cout<<endl;
}
used[(i-1)/4+1][(i-1)%4+1]=1;
cnt=1;
dfs((i-1)/4+1,(i-1)%4+1);
printf("s=%d\n",sum);
}
}
}
}
}
cout<<sum<<endl;
return 0;
}
第八题 四平方和
枚举一下,但是要注意时间的问题。我也是直接用三层循环,反向判断。O(n^3)的时间复杂度。应该是能过了。但是还有种时间复杂度更低的方法。先将两个数字的和的情况全部枚举出来,放进一个数组,然后排序。最后两重循环二分查找是否再
数组中又n-a-b的值。但是太麻烦了,有简单的就用简单的。
#include <iostream>
#include <cstdio>
#include <cmath>
#define max 2300
using namespace std;
int main()
{
int n,i,j,k,l;
cin>>n;
for(i=0; i<max; i++)
{
if(i*i<=n)
for(j=0; j<max; j++)
{
if(i*i+j*j<=n)
for(k=0; k<max; k++)
{
if(i*i+j*j+k*k>n)continue;
int m=sqrt(n-i*i-j*j-k*k);
if(i*i+j*j+k*k+m*m==n){
printf("%d %d %d %d\n",i,j,k,m);
return 0;
}
}
}
}
return 0;
}
第九题 交换瓶子
看到这道题,我的第一想法是,检查,如果瓶子号和位置相等,除去。如果两个瓶子的号和位置正好互相一样,sum+1,然后除去。最后剩下直接的k个,k-1次就可以互换完全。当时脑子一片混乱,就没想出来,害怕复杂度太高会超时,因为查的时候要两重循环。然后想想与其这样查,不如直接两重循环每回都把当前位置需要的换回来,发现也是ok的。但是估计真到考试这道题也是得不满的。这道题我花了许久时间。真到考试,可能第十题都没时间看了。
#include <iostream>
#include <cstdio>
using namespace std;
int a[1002],p[1002];
int main()
{
int n,i,j,s=0;
cin>>n;
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
// p[a[i]]=i;
p[i]=i;//保存位置
}
for(i=1;i<=n;i++){
if(a[i]!=p[i]){
for(j=1;j<=n;j++){
if(a[j]==p[i]){//找到和该位置号相同的瓶子
int t=a[j];//交换
a[i]=a[j];
a[j]=t;
s++;
}
}
}
}
cout<<s<<endl;
return 0;
}
我写的不太好,但是找到个厉害的。一重循环就搞定了。
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
int N,sum=0;
int d[10010];
int a[10010];
int i,j,temp;
scanf("%d",&N);
for(i=1;i<=N;i++)
{
cin>>a[i];//a是i位置的瓶子号
d[a[i]]=i;//d是记录某个瓶子放在了多少位置,方便直接找,省去一重循环/
}
for(i=1;i<=N;i++)
{
if(d[i]!=i)
{
temp=a[i];
a[i]=a[d[i]];//d[i]代表i这个瓶子放的位置。 这一点其实很不容易想,容易绕晕人。
a[d[i]]=temp;
d[temp]=d[i];
d[i]=i;
sum++;
}
}
cout<<sum<<endl;
return 0;
}
第十题 最大比例
xi好tm大,必须用longlong了。数组也要开longlong的。这道题我的思路是直接把数组从小到大排列。然后找一个从第一项开始,往后如果和第一项不同了(记为第K项),则求他们的最小公约数g。然后第一项和第K项同时除以g。这个当作他们的比例。然后第K项开始,往后推,每个都乘这个比例。如果不满足,则退出,分子分母同时开根号。继续试。都满足了,得到答案。但是问题就在,我在网上看到的题目中没有给出N的范围。并且没有蓝桥杯官网的vip,所以没法试验。但是给了3s,应该也许大概可能可以通过吧。我觉得思路应该没问题。结果应该是正确的。
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
ll a[102];
ll gcd(ll a,ll b){
if(b==0)return a;
else return gcd(b,a%b);
}
int main()
{
int n,i;
scanf("%d",&n);
for(i=0;i<n;i++){
scanf("%I64d",&a[i]);
}
sort(a,a+n);
ll aa,bb,mm;
for(i=1;i<n;i++){
if(a[i]!=a[0]){
ll t=gcd(a[i],a[0]);
//printf("t=%I64d\n",t);
aa=a[i]/t;
bb=a[0]/t;
break;
}
}
printf("%I64d,%I64d\n",aa,bb);
while(1){
bool flag=true;
for(int j=i+1;j<n;j++){
mm=a[j-1];
while(a[j]>mm){
mm=mm*aa/bb;
//printf("a[%d]=%I64d,mm=%I64d\n",j,a[j],mm);
}
printf("j=%d,mm=%I64d\n",j,mm);
if(mm!=a[j]){
flag=false;
break;
}
}
if(flag)break;
else {
printf("开根号\n");
aa=sqrt(aa);
bb=sqrt(bb);
}
}
printf("%I64d,%I64d\n",aa,bb);
return 0;
}
感想
自己做题还考试是完全不一样的感觉,特别是我这种容易紧张的弱鸡,没准直接心态炸了。况且题是一年比一年难,所以我还是报b组吧。毕竟初入,头铁没有好下场,君不见三选奥巴马,五放加里奥。觉得会和会是两码事,多练是真的。
写博客呢,是想记录一下,要不然都得忘了。
感觉写博客会让人进步= = 但是我懒...