感觉在这里码字不太习惯,所有的解题思路都在代码注释里面了
1、愤怒小鸟
X星球愤怒的小鸟喜欢撞火车!
一根平直的铁轨上两火车间相距 1000 米
两火车 (不妨称A和B) 以时速 10米/秒 相对行驶。
愤怒的小鸟从A车出发,时速50米/秒,撞向B车,
然后返回去撞A车,再返回去撞B车,如此往复…
两火车在相距1米处停车。
问:这期间愤怒的小鸟撞 B 车多少次?
注意:需要提交的是一个整数(表示撞B车的次数),不要填写任何其它内容。
源码
package 第七届蓝桥杯决赛真题;
//思路:这道题好像没找到别的方法,干脆就直接模拟全过程,反正这点计算量对计算机来说都不是事。
public class question1_1 {
public static void main(String args[]) {
float a=0; //火车a的位置
float b=1000; //火车b的位置
float c=0; //小鸟的位置
float speed1=10; //火车速度
float speed2=50; //小鸟速度
//模拟第一次发生撞击,每次撞击后更新当前状态信息,火车位置,小鸟位置,撞击时间
float time=(b-c)/(speed1+speed2); //撞击发生的时间
a=speed1*time; //撞击后a所在的位置
b=1000-(speed1*time); //撞击后b所在的位置
c=speed2*time; //撞击后c所在的位置
int ans=1; //已经撞击过一次
int i=1; //i=1表示要和b相撞,i=-1表示要和a相撞
while ((b-a)>1) {
i*=-1; //小鸟和每个火车相撞后,都会更换目标
if(i==1) {
ans++; //只在撞击B时才会将次数增加一
time+=(b-c)/(speed1+speed2);
c+=(b-c)/(speed1+speed2)*speed2;
a=speed1*time;
b=1000-(speed1*time);
}
if(i==-1) {
time+=(c-a)/(speed1+speed2);
c-=(c-a)/(speed1+speed2)*speed2;
a=speed1*time;
b=1000-(speed1*time);
}
}
System.out.printf("总共撞击B次数ans:%d\n", ans);
}
}
答案:9
2、反幻方
我国古籍很早就记载着
2 9 4
7 5 3
6 1 8
这是一个三阶幻方。每行每列以及对角线上的数字相加都相等。
下面考虑一个相反的问题。
可不可以用 1~9 的数字填入九宫格。
使得:每行每列每个对角线上的数字和都互不相等呢?
这应该能做到。
比如:
9 1 2
8 4 3
7 5 6
你的任务是搜索所有的三阶反幻方。并统计出一共有多少种。
旋转或镜像算同一种。
比如:
9 1 2
8 4 3
7 5 6
7 8 9
5 4 1
6 3 2
2 1 9
3 4 8
6 5 7
等都算作同一种情况。
请提交三阶反幻方一共多少种。这是一个整数,不要填写任何多余内容。
旋转有四种
镜像4种
全排列除以8
源码
package 第七届蓝桥杯决赛真题;
import java.util.HashSet;
import java.util.Set;
/*
这道题就是利用全排列来求解,全排列的求解方法网上有一大堆,不做赘述
通过全排列和检查就可以解决这道问题
检查时有个比较好的方法:
行,列,斜线一共有八个,八个数字两两不相等有28种写法,不太好些,我们可以直接利用Set集和来判断,利用集和中重复元素只能存在一次的性质,通过集和长度来判断是否有相同元素
要注意的就是九宫格的旋转和镜像有8种状态,将计算出的结果除以八
旋转四种,旋转90°,180°,270°,加上自身,镜像四种有四条对称轴
*/
public class question2 {
static int ans=0;
public static void main(String args[]) {
int[] num= {1,2,3,4,5,6,7,8,9};
f(num,0,num.length-1);
System.out.println(ans/8); //可以将最后的结果对8求余来验证是否正确,求余结果一定为0
}
private static void f(int[] num, int start, int length) {
if(start==num.length-1) {
if (check(num)) {
ans++;
}
}
for (int i = start; i < num.length; i++) {
swap(num, i, start);
f(num,start+1,num.length);
swap(num, i, start);
}
}
//检查,行,列,斜线一共有八个,八个数字两两不相等有28种,我们可以直接利用Set集和来判断,利用集和中重复元素只能存在一次的性质,通过集和长度来判断是否有相同元素
private static boolean check(int[] num) {
Set<Integer> set=new HashSet<Integer>();
int a1=num[0]+num[1]+num[2];
int a2=num[3]+num[4]+num[5];
int a3=num[6]+num[7]+num[8];
int a4=num[0]+num[3]+num[6];
int a5=num[1]+num[4]+num[7];
int a6=num[2]+num[5]+num[8];
int a7=num[0]+num[4]+num[8];
int a8=num[2]+num[4]+num[6];
set.add(a1);
set.add(a2);
set.add(a3);
set.add(a4);
set.add(a5);
set.add(a6);
set.add(a7);
set.add(a8);
if(set.size()==8)
return true;
return false;
}
//交换数组中的两个指定下标的元素
private static void swap(int[] a, int i, int start) {
int b=a[i];
a[i]=a[start];
a[start]=b;
}
}
答案:3120
3、
打靶
小明参加X星球的打靶比赛。
比赛使用电子感应计分系统。其中有一局,小明得了96分。
这局小明共打了6发子弹,没有脱靶。
但望远镜看过去,只有3个弹孔。
显然,有些子弹准确地穿过了前边的弹孔。
不同环数得分是这样设置的:
1,2,3,5,10,20,25,50
那么小明的6发子弹得分都是多少呢?有哪些可能情况呢?
下面的程序解决了这个问题。
仔细阅读分析代码,填写划线部分缺失的内容。
public class Main
{
static void f(int[] ta, int[] da, int k, int ho, int bu, int sc)
{
if(ho<0 || bu<0 || sc<0) return;
if(k==ta.length){
if(ho>0 || bu>0 || sc>0) return;
for(int i=0; i<da.length; i++){
for(int j=0; j<da[i]; j++)
System.out.print(ta[i] + " ");
}
System.out.println();
return;
}
for(int i=0; i<=bu; i++){
da[k] = i;
f(ta, da, k+1, __________________ , bu-i, sc-ta[k]*i); // 填空位置
}
da[k] = 0;
}
public static void main(String[] args)
{
int[] ta = {1,2,3,5,10,20,25,50};
int[] da = new int[8];
f(ta, da, 0, 3, 6, 96);
}
}
注意:只填写划线处缺少的内容,不要填写已有的代码或符号,也不要填写任何解释说明文字等。
源码
//代码填空题,没什么好解释的,Debug就可以了,这道题知识有点绕
public class question3 {
/*
* ta:代表不同环数的得分
* da:da[i],代表在第i环上打了da[i]枪
* k:表示接下来打来讨论第k环打了多少次
* ho:表示目前还剩下几个孔没讨论
* bu:表示目前打了6-bu枪,即还能打机枪
* sc:表示目前已经打了96-sc分
*/
static void f(int[] ta, int[] da, int k, int ho, int bu, int sc) {
if (ho < 0 || bu < 0 || sc < 0)
return;
if (k == ta.length) {
if (ho > 0 || bu > 0 || sc > 0)
return;
for (int i = 0; i < da.length; i++) {
for (int j = 0; j < da[i]; j++)
System.out.print(ta[i] + " ");
}
System.out.println();
return;
}
for (int i = 0; i <= bu; i++) {
da[k] = i;
// 如果第ta[i]换不打,即i=0,则讨论下一枪的情况时,还有ho个孔没打;若i!=0,则讨论下一枪的情况时,有ho-1个孔还没打。
f(ta, da, k + 1, ho - (i == 0 ? 0 : 1), bu - i, sc - ta[k] * i); // 填空位置
}
da[k] = 0;
}
public static void main(String[] args) {
int[] ta = { 1, 2, 3, 5, 10, 20, 25, 50 };
int[] da = new int[8];
f(ta, da, 0, 3, 6, 96);
}
}
答案:ho - (i == 0 ? 0 : 1)
4、路径之谜
小明冒充X星球的骑士,进入了一个奇怪的城堡。
城堡里边什么都没有,只有方形石头铺成的地面。
假设城堡地面是 n x n 个方格。【如图1.png】所示。
按习俗,骑士要从西北角走到东南角。
可以横向或纵向移动,但不能斜着走,也不能跳跃。
每走到一个新方格,就要向正北方和正西方各射一箭。
(城堡的西墙和北墙内各有 n 个靶子)
同一个方格只允许经过一次。但不必做完所有的方格。
如果只给出靶子上箭的数目,你能推断出骑士的行走路线吗?
有时是可以的,比如图1.png中的例子。
本题的要求就是已知箭靶数字,求骑士的行走路径(测试数据保证路径唯一)
输入:
第一行一个整数N(0<N<20),表示地面有 N x N 个方格
第二行N个整数,空格分开,表示北边的箭靶上的数字(自西向东)
第三行N个整数,空格分开,表示西边的箭靶上的数字(自北向南)
输出:
一行若干个整数,表示骑士路径。
为了方便表示,我们约定每个小格子用一个数字代表,从西北角开始编号: 0,1,2,3…
比如,图1.png中的方块编号为:
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
示例:
用户输入:
4
2 4 3 4
4 3 3 3
程序应该输出:
0 4 5 1 2 3 7 11 10 9 13 14 15
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。
源码
package 第七届蓝桥杯决赛真题;
import java.util.Scanner;
/*
解题思路:
递归+回溯+判断
根据题目要去,我们要做的是寻找每一条可以从起点到达终点的路径,然后对我们查找到的路径进行分析比较,将正确的路径输出。
处理用户输入:
定义一个int变量n来表示地图的尺寸,声明两个int数据用来存储两边靶子上的箭的个数,作为全局变量,供检查的时候调用。
查找路径:
使用dfs(深度优先搜索)通过递归和回溯找到每一条路径,将路径上每一个格子对应的编号存储在数组中,根据题目要求,一个格子只能走一次,所以需要设置一个二维布尔数组吗,用来标记格子是否被走过,在回溯的时候要更改标记,
递归结束的条件是走到了终点。
检查路径
根据题意,每行走到一个格子,就要向两边的靶子上各射一箭,在遍历路径的时候,对于行走过的每一个格子,求出横纵坐标,将对应的两个靶子上的箭的个数增加1,这一步使用两个一位数组表示,最后和用户输入进行比较,即可判断当前路径是否正确
*/
public class question4 {
static int[][] map; //全局变量,地图,即每个格子的编号
static boolean[][] flag; //全局变量,标记格子是否被走过
static int[] north; //全局变量,存储用户输入,表示北边的箭靶
static int[] south; //全局变量,存储用户输入,表示北边的箭靶
public static void main(String args[]) {
//处理用户输入
Scanner in=new Scanner(System.in);
int n=in.nextInt();
north=new int[n];
south=new int[n];
for (int i = 0; i < n; i++) {
north[i]=in.nextInt();
}
for (int i = 0; i < n; i++) {
south[i]=in.nextInt();
}
map=new int[n+1][n+1];
flag=new boolean[n+1][n+1];
for (int i = 0; i < n; i++) { //初始化地图,即给地图进行编号
for (int j = 0; j < n; j++) {
map[i][j]=i*4+j;
}
}
int[] path=new int[n*n]; //记录路径,地图的范围为n*n,所以路径最长为n*n
int count=0; //记录路径长度
flag[0][0]=true;
path[count++]=map[0][0]; //0,0位置是起始位置,直接将其加入的路径中,并标记为已走过
dfs(0,0,n,path,count); //使用dfs算法解决本题
}
private static void dfs(int x, int y, int n, int[] path, int count) { //x,y是当前位置,n是地图的长度,path是路径,count是路径长度
int[] line= {1,0,-1,0};
int[] row= {0,1,0,-1}; //这两个数组表示每次可以走的范围,上下左右
if(x==n-1&&y==n-1) { //递归结束条件,走到终点
//递归结束执行检查
if(check(path,count,n)) { //路径符合要求,输出
for (int i = 0; i < count; i++) {
System.out.print(path[i]+" ");
}
System.out.println("");
}
}
//递归四周的点
for (int i = 0; i < 4; i++) {
int now_x=x+line[i]; //求接下来一个节点的坐标
int now_y=y+row[i];
if(now_x<0||now_x>(n-1)||now_y<0||now_y>(n-1)) //位置不在地图内
continue;
if(flag[now_x][now_y]) //位置已经被经过
continue;
flag[now_x][now_y]=true; //标记为已走过
path[count++]=map[now_x][now_y]; //添加到路径当中
dfs(now_x, now_y, n, path, count); //递归寻找路径
flag[now_x][now_y]=false; //回溯,清空对这一个点的标记
count-=1; //路径长度减一,表示退出这个格子,将其在路径中删除
}
}
//路径检查
private static boolean check(int[] path, int count, int n) {
int[] line=new int[n]; //表示当前这个路径射到的箭靶
int[] row=new int[n];
for (int i = 0; i < count; i++) { //求每一个格子对应的箭靶,更新箭靶状态
int x=path[i]/4;
int y=path[i]%4;
line[y]+=1;
row[x]+=1;
}
boolean ans=true;
for (int i = 0; i < n; i++) {
if(north[i]!=line[i]||south[i]!=row[i]) { //如果有一个箭靶不符合要求,本条路径判断为不正确
ans=false;
continue;
}
}
return ans;
}
}
后两道题目前还没有找到较好的解决办法,如果有解决的话会分享出来。