第一题 :煤球数目
题目叙述:
有一堆煤球,堆成三角棱锥形。具体:
第一层放1个,
第二层3个(排列成三角形),
第三层6个(排列成三角形),
第四层10个(排列成三角形),
….
如果一共有100层,共有多少个煤球?
请填表示煤球总数目的数字。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
答案:171700
规律比较好找,每层煤球的个数是(1+2+ ... + n) 也就是 (n+1)*n/2;
100层求和即可。
#include <iostream> using namespace std; int main() { long long res = 0; for ( int i = 1 ; i <= 100 ; ++ i ) { res += (i+1)*i/2; } cout << res << endl; return 0; }
第二题 :生日蜡烛
题目叙述:
某君从某年开始每年都举办一次生日party,并且每次都要吹熄与年龄相同根数的蜡烛。
现在算起来,他一共吹熄了236根蜡烛。
请问,他从多少岁开始过生日party的?
请填写他开始过生日party的年龄数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
答案:26
没什么技巧,直接3层暴力枚举年龄,过生日年份,累加求和,判断和是不是236。
不过问题就是万一,某君长寿咋整,恰好今年236过一次生日。不说了,尴尬症犯了。
#include <iostream> using namespace std; int main() { for ( int i = 1 ; i < 236 ; ++ i ) { for ( int j = 1 ; j < 236 ; ++ j ) { int ans = 0, cnt = i; for ( int k = 1 ; k < j ; ++ k ) { ans += cnt++; } if ( ans == 236 ) { cout << i << endl; return 0; } } } return 0; }
第三题 :凑算式
题目叙述:
B DEF
A + — + ——— = 10
C GHI
这个算式中A~I代表1~9的数字,不同的字母代表不同的数字。
比如:
6+8/3+952/714 就是一种解法,
5+3/1+972/486 是另一种解法。
这个算式一共有多少种解法?
注意:你提交应该是个整数,不要填写任何多余的内容或说明性文字。
正确答案:29
这里枚举还是有些技巧的,我这里只是对全排列稍微做了一些优化,没有做数学上的分析,比如根据A的范围可以大致确定后两个数的大小,这样可以有效提高算法很多,但是分析过程也很复杂烧脑。
#include <iostream> #include <algorithm> using namespace std; int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; int gcd( int a, int b ) { while ( a && b ) a>b?a%=b:b%=a; return a+b; } int main() { int ans = 0; do { int n0 = a[0]; int n1 = a[1], n2 = a[2]; int n3 = a[3]*100+a[4]*10+a[5]; int n4 = a[6]*100+a[7]*10+a[8]; // 先大致算出来结果范围 按照程序除法不留小数 结果应该在 8.xx ~ 10 int gus = n0+n1/n2+n3/n4; if ( gus > 10 || gus < 8 ) continue; // 如果n2 n4互质 则没有通分 直接算结果 if ( gcd(n2, n4) == 1 ) { if ( n1/n2*n2 != n1 ) continue; if ( n3/n4*n4 != n3 ) continue; if ( gus == 10 ) ans ++; } else { // 否则要通分 这里根据公式把除法转换成了乘法 // 其实这个式子对是否需要通分都合适 所以直接判断这里就可以得出正确答案 if ( n1*n4 == ((10-n0)*n4-n3)*n2 ) { ans ++; } } } while ( next_permutation(begin(a), end(a)) ); cout << ans << endl; return 0; }
第四题 :快速排序
题目叙述:
排序在各种场合经常被用到。
快速排序是十分常用的高效率的算法。
其思想是:先选一个“标尺”,
用它把整个队列过一遍筛子,
以保证:其左边的元素都不大于它,其右边的元素都不小于它。
这样,排序问题就被分割为两个子区间。
再分别对子区间排序就可以了。
下面的代码是一种实现,请分析并填写划线部分缺少的代码。
#include <stdio.h> void swap(int a[], int i, int j) { int t = a[i]; a[i] = a[j]; a[j] = t; } int partition(int a[], int p, int r) { int i = p; int j = r + 1; int x = a[p]; while(1){ while(i<r && a[++i]<x); while(a[--j]>x); if(i>=j) break; swap(a,i,j); } /*在这里填写必要的代码*/; return j; } void quicksort(int a[], int p, int r) { if(p<r) { int q = partition(a,p,r); quicksort(a,p,q-1); quicksort(a,q+1,r); } } int main() { int i; int a[] = {5,13,6,24,2,8,19,27,6,12,1,17}; int N = 12; quicksort(a, 0, N-1); for(i=0; i<N; i++) printf("%d ", a[i]); printf("\n"); return 0; }
正确答案:swap(a,p,j)
快排,编程必备算法。
第五题 :抽签
题目叙述:
X星球要派出一个5人组成的观察团前往W星。
其中:
A国最多可以派出4人。
B国最多可以派出2人。
C国最多可以派出2人。
….
那么最终派往W星的观察团会有多少种国别的不同组合呢?
下面的程序解决了这个问题。
数组a[] 中既是每个国家可以派出的最多的名额。
程序执行结果为:
DEFFF
CEFFF
CDFFF
CDEFF
CCFFF
CCEFF
CCDFF
CCDEF
BEFFF
BDFFF
BDEFF
BCFFF
BCEFF
BCDFF
BCDEF
….
(以下省略,总共101行)
#include <stdio.h> #define N 6 #define M 5 #define BUF 1024 void f(int a[], int k, int m, char b[]) { int i,j; if(k==N){ b[M] = 0; if(m==0) printf("%s\n",b); return; } for(i=0; i<=a[k]; i++){ for(j=0; j<i; j++) b[M-m+j] = k+'A'; /*在这里填写必要的代码*/; } } int main() { int a[N] = {4,2,2,1,1,3}; char b[BUF]; f(a,0,M,b); return 0; }答案: f(a,k+1,m-i,b) (或者 f(a,k+1,m-i,b) ,因为 i 和 j 相等)
似乎每年蓝桥杯省赛都会出一个递归的填空题。
第六题 :方格填数
题目叙述:
如下的10个格子
填入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)
一共有多少种可能的填数方案?
请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
答案:1580
#include <iostream> #include <cmath> using namespace std; #define inf 100 int a[3][4]; int res = 0; bool valid( int x, int y ) { if ( x==0 && y==0 ) return false; if ( x==2 && y==3 ) return false; return x>=0 && x<3 && y>=0 && y<4; } bool ok(int x, int y) { for ( int i = -1 ; i <= 1 ; ++ i ) { for ( int j = -1 ; j <= 1 ; ++ j ) { if ( i == 0 && j == 0 ) continue; if ( valid(x+i, y+j) ) { if ( abs(a[x+i][y+j] - a[x][y]) == 1 ) return false; } } } return true; } void dfs( int k ) { if ( k == 10 ) { res ++; } else { for ( int i = 0 ; i < 3 ; ++ i ) { for ( int j = 0 ; j < 4 ; ++ j ) { if ( a[i][j] == inf && valid(i,j) ) { a[i][j] = k; if ( ok(i,j) ) dfs( k+1 ); a[i][j] = inf; } } } } } int main() { for ( int i = 0 ; i < 3 ; ++ i ) { for ( int j = 0 ; j < 4 ; ++ j ) { a[i][j] = inf; } } dfs( 0 ); cout << res << endl; return 0; }
第七题 :剪邮票
题目叙述:
如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。
请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
但是深搜不会拐弯, 所以会少算一些情况,例如:
- - o - 或者 o o o o
o o o - - o - -
- o - - - - - -
圆圈为邮票 -为空白
深搜的结果应该是82,贴下自己错误的代码,以示警告:
#include <iostream> #include <set> #include <string> using namespace std; int vis[3][4]; int ans; set<string> s; int dx[] = { 1, 0, -1, 0 }; int dy[] = { 0, 1, 0, -1 }; bool valid( int x, int y ) { return (x>=0 && x<3 && y>=0 && y<4); } void dfs( int x, int y, int step ) { if ( step >= 5 ) { string str = ""; for ( int i = 0 ; i < 3 ; ++ i ) { for ( int j = 0 ; j < 4 ; ++ j ) { if( vis[i][j] ) { str += 'a'+ i*4+j; } } } ans ++; // cout << str << endl; s.insert( str ); } else { for ( int i = 0 ; i < 4 ; ++ i ) { int nx = x+dx[i]; int ny = y+dy[i]; if ( valid(nx,ny) && vis[nx][ny] == 0 ) { vis[nx][ny] = 1; dfs( nx, ny, step+1 ); vis[nx][ny] = 0; } } } } int main() { std::ios::sync_with_stdio(false); for ( int i = 0 ; i < 3 ; ++ i ) { for ( int j = 0 ; j < 4 ; ++ j ) { vis[i][j] = 1; dfs(i,j,1); vis[i][j] = 0; } } // 深搜结果不对 因为有些情况搜索不到 // - - o - 或者 o o o o // o o o - - o - - // - o - - - - - - // 圆圈为邮票 -为空白 cout << s.size() << endl; return 0; }
下面是正确解法。
#include <iostream> #include <cstring> using namespace std; int a, b, c, d, e; int arr[15]; int dx[] = { -5, -1, 1, 5 }; bool valid( int x ) { return x>=1 && x<=14 && x!=5 && x!=10; } bool zero() { for ( int i = 1 ; i < 15 ; ++ i ) { if ( arr[i] ) return false; } return true; } void dfs( int x ) { if (zero()) { return ; } else { for( int i = 0 ; i < 4 ; ++ i ) { int nx = x+dx[i]; if ( valid(nx) && arr[nx] ) { arr[nx] = 0; dfs( nx ); } } } } // 从一个点深搜 能到达的点都置0 如果最后数组为全0 即说明胡连 // 左右相邻为 +1 -1 // 上下相邻为 +5 -5 bool ok() { memset( arr, 0, sizeof(arr) ); arr[b] = arr[c] = arr[d] = arr[e] = 1; dfs(a); if ( zero() ) return true; return false; } /* +---+---+---+---+ | 1 | 2 | 3 | 4 | +---------------+ | 6 | 7 | 8 | 9 | +---------------+ | 11| 12| 13| 14| +---------------+ */ int main() { int ans = 0; for ( a = 1 ; a <= 14 ; ++ a ) { if ( a == 5 || a == 10 ) continue; for ( b = a+1 ; b <= 14 ; ++ b ) { if ( b == 5 || b == 10 ) continue; for ( c = b+1 ; c <= 14 ; ++ c ) { if ( c == 5 || c == 10 ) continue; for ( d = c+1 ; d <= 14 ; ++ d ) { if ( d == 5 || d == 10 ) continue; for ( e = d+1 ; e <= 14 ; ++ e ) { if ( e == 5 || e == 10 ) continue; if ( ok() ) { // cout << a <<" " << b << " " << c << " " << d << " " << e << endl; ans ++; } } } } } } cout << ans << endl; return 0; }
第八题 :四平方和
题目叙述:
四平方和定理,又称为拉格朗日定理:
每个正整数都可以表示为至多4个正整数的平方和。
如果把0包括进去,就正好可以表示为4个数的平方和。
比如:
5 = 0^2 + 0^2 + 1^2 + 2^2
7 = 1^2 + 1^2 + 1^2 + 2^2
(^符号表示乘方的意思)
对于一个给定的正整数,可能存在多种平方和的表示法。
要求你对4个数排序:
0 <= a <= b <= c <= d
并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法
程序输入为一个正整数N (N<5000000)
要求输出4个非负整数,按从小到大排序,中间用空格分开
例如,输入:
5
则程序应该输出:
0 0 1 2
再例如,输入:
12
则程序应该输出:
0 2 2 2
再例如,输入:
773535
则程序应该输出:
1 1 267 838
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 3000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型。
这题本来想 用a+b+c+d = x;然后预处理出两个数的和;则问题变成A+B = x;
然后枚举 A,即可二分查找 x-A是否存在。当存在时,使用一个简单的hash就可以还原出原数字。
我处理hash的方法是:map[val] = i*10000+j;因为通过计算可知sqrt(5000000)=2237。
都是四位数字,可以使用第四位保存一个数,高位保存一个数。最后即可还原出平方和公式。
但是在处理两个数的和的时候 是一个2237*2237的循环,还要保存下来,然后还要处理重复,排序。
我试过了用set,vector,数组,效率都很低,不知道是不是和电脑当时的环境有关,最后放弃了这种做法。
选择了直接暴力枚举,简单粗暴,还比较有效。
#include <iostream> using namespace std; int main() { int n; int cnta, cntb, cntc, cntd; cin>>n; for(int a=0;a<=2237;a++) { cnta = a*a; if(cnta>n) continue; for(int b=a;b<=2237;b++) { cntb = b*b; if(cnta+cntb>n) break; for(int c=b;c<=2237;c++) { cntc = c*c; if(cnta+cntb+cntc>n) break; for(int d=c;d<=2237;d++) { cntd = d*d; if(cnta+cntb+cntc+cntd > n) break; if(cnta+cntb+cntc+cntd==n) { cout<<a<<" "<<b<<" "<<c<<" "<<d<<endl; return 0; } } } } } return 0; }
第九题 :交换瓶子
题目叙述:
有N个瓶子,编号 1 ~ N,放在架子上。
比如有5个瓶子:
2 1 3 5 4
要求每次拿起2个瓶子,交换它们的位置。
经过若干次后,使得瓶子的序号为:
1 2 3 4 5
对于这么简单的情况,显然,至少需要交换2次就可以复位。
如果瓶子更多呢?你可以通过编程来解决。
输入格式为两行:
第一行:一个正整数N(N<10000), 表示瓶子的数目
第二行:N个正整数,用空格分开,表示瓶子目前的排列情况。
输出数据为一行一个正整数,表示至少交换多少次,才能完成排序。
例如,输入:
5
3 1 2 5 4
程序应该输出:
3
再例如,输入:
5
5 4 3 2 1
程序应该输出:
2
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型。
简单的贪心,不在自己该在的位置,就和那个位置换一下。
#include <iostream> #include <cstdio> using namespace std; int a[10010]; int main() { int n; scanf( "%d", &n ); for ( int i = 1 ; i <= n ; ++ i ) { scanf( "%d", &a[i] ); } int ans = 0; for ( int i = 1 ; i <= n ; ) { if ( a[i] != i ) { swap( a[i], a[a[i]] ); ans ++; } else { i ++; } } printf( "%d\n", ans ); return 0; }
第十题 :最大比例
题目叙述:
X星球的某个大奖赛设了M级奖励。每个级别的奖金是一个正整数。
并且,相邻的两个级别间的比例是个固定值。
也就是说:所有级别的奖金数构成了一个等比数列。比如:
16,24,36,54
其等比值为:3/2
现在,我们随机调查了一些获奖者的奖金数。
请你据此推算可能的最大的等比值。
输入格式:
第一行为数字N,表示接下的一行包含N个正整数
第二行N个正整数Xi(Xi<1 000 000 000 000),用空格分开。每个整数表示调查到的某人的奖金数额
要求输出:
一个形如A/B的分数,要求A、B互质。表示可能的最大比例系数
测试数据保证了输入格式正确,并且最大比例是存在的。
例如,输入:
3
1250 200 32
程序应该输出:
25/4
再例如,输入:
4
3125 32 32 200
程序应该输出:
5/2
再例如,输入:
3
549755813888 524288 2
程序应该输出:
4/1
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 3000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型。