Java B组蓝桥杯第十届国赛:切割

38 篇文章 1 订阅
32 篇文章 2 订阅

试题 C: 切割
本题总分:10 分
【问题描述】
在 4×4 的方格矩阵中画一条直线。则直线穿过的方格集合有多少种不同的
可能?
这个里直线穿过一个方格当且仅当直线将该方格分割成面积都大于 0 的两
部分。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

这个题网上没有找到大佬解决,硬着头皮自己试了半天。最后采用坐标系,进行筛选判断。

看图,分成16格,每个格子给它起个名字,任意起名字。

首先,它肯定是有旋转性质的,我们先求出一边的结果数,就能直接得出4边的多少种。我把每一格分成100刻度,当然分的更多更精确。

这里先求直线从底边进入的情况:

该情况又分为三种:从哪个边出去。

(1)从最上边出去:如图得到两个交点(x1,y1),(x2,y2)

判断该直线上每一点坐标在哪个区间,然后收集格子上的字符,采用判重只收集一次。

收集完之后存入set,累计数量。

任意一点坐标怎么求,这都是数学的基本公式了,利用y轴刻度  y 作为参照(0~400),那么对应x坐标为:double x = x2 + (x1 - x2) / 400 * y;

最外层遍历x1(0~400),x2(0~400)。时间复杂度n^3。

(2)从最左边出去

固定x1=0不变,然后x2和y均为(0~400)遍历,收集,和(1)类似

(3)从最右边出去

固定x1=400不变,然后x2和y均为(0~400)遍历,收集,和(2)一样

最后得到结果:

1
15
159
159d
2
21
215
2159
2159d
259d
2659d
259
2659
25
269d
3
32
321
3215
325
3265
32659
32659d
3269d
326a9d
36a9d
376a9d
3269
369
3659
4
42
432
4321
43215
4325
43265
432659
43659
43769
4376a9
4376a9d
437659
4365
4376ad
47ad

 


476a9d
47a9d

26a9d
437aed
47aed
487baed
4376aed

36aed
26ad
26aed
37aed
26ae
326ae
36ae
376ae
37ae
437bae
47bae
487bae
159de
159e
159ae
15ae
37bae
156ae
16ae
126ae
487be
487bfe
437be
47be
437bfe
47bfe
37be
37bfe
48bfe
37bf
437bf
47bf
487bf
48bf
15aef
156aef
126aef
 

26aef
26af
26abf
26bf
48cbf
16aef
267bf

47baed
437baed
376ad
376aed
326ad
36ad
326aed

27bf
237bf
126af
126abf
16af
156af
16abf
156abf
48cf
48cgf
48cg

156abfg
16abfg
126abfg
126bfg
26bfg
267bfg
237bfg
37bfg
37bg
37bcg
37cg
27bfg
378cg
1267bfg
38cg
348cg
237bg
237bcg
 

27bg
267bg
27bcg
267bcg
1267bg
1267bcg
16bfg
23

1234
12348
1238
12378
1278
12678c

1267c
1267bc
167bcg
16bcg
16bg
13
123
1278c
12378c
234
2348
238
2378
2378c

278c
27c
237c
237cg
2378cg
34
348
38
378c
38c
348c
48
48c

我的结果大概就这么多,这只是从底边进入的直线得到的。

统计情况:

统计以某字符开头的数量

即,从底边哪个格子进入

 
144                                    
243
343
444

我大概用图测试了上面很多的结果,都是ok的,而且是没有重复的。

一个边结果一共为174种可能

那么四个边就为696种。

那接着我们再来考虑重复的结果:

我们拿出单个直线来分析,就拿图上的那根直线来看。它经过的那个格子的集合,只有两种的情况会存在,那就是直线  下进上出   和   上进下出。

那我们推广到任意一条直线,结论就是:

任意一条直线,其出现的情况只有两种,那就是分别从直线两端进入到另一端出去。两种情况经过的集合是一种集合。

也就是说在696种结果中,如果不分方向,按集合来算的话,那就是有一半结果是重复的。

因此得到结果348种。

一顿分析猛如虎,我也不知道答案对不对,但思路应该是没啥大问题的吧

代码:整个代码就是在计算每条直线上每个点所在的位置,记录整个直线经过的格子集合,以字符串的形式存放起来,最后统计结果。似乎没啥好看的

import java.util.HashSet;
import java.util.Set;

public class Main {

    //方格字符标记
	String[][] ss = { { "1", "2", "3", "4" }, { "5", "6", "7", "8" }, { "9", "a", "b", "c" }, { "d", "e", "f", "g" } };
    //结果集合
	Set<String> set = new HashSet<String>();
    
	int a = 0, b = 0, c = 0, d = 0;

	public Main() {
        //遍历x1,x2
		for (int x1 = 0; x1 <= 400; x1++) {
			for (int x2 = 0; x2 <= 400; x2++) {
				// System.out.println("x1="+x1+",x2="+x2);
				if (x1 == 0 || x1 == 400) {
					// 固定x1,直线从侧边出去的结果
					getlist4(x1, x2);
				}
				// 固定y=400,直线从顶部出去的数量结果
				getlist(x1, x2);
			}
		}
		System.out.println(a);
		System.out.println(b);
		System.out.println(c);
		System.out.println(d);
		System.out.println(set.size());

	}

	// 分别统计字符开头的数据,用于观察
	public void count(String s) {
		if (s.startsWith("1")) {
			a++;
		} else if (s.startsWith("2")) {
			b++;
		} else if (s.startsWith("3")) {
			c++;
		} else if (s.startsWith("4")) {
			d++;
		}
	}

	// 求顶部结果
	public void getlist(double x1, double x2) {
		String s = "";
		for (int i = 0; i < 400; i++) {
			double x = x2 + (x1 - x2) / 400 * i;
			if (x > 0 && x < 100) {// 第一格内
				if (s.equals("") || !s.substring(s.length() - 1, s.length()).equals(ss[i / 100][0])) {
					s += ss[i / 100][0];
				}
			} else if (x > 100 && x < 200) {// 第二格内
				if (s.equals("") || !s.substring(s.length() - 1, s.length()).equals(ss[i / 100][1])) {
					s += ss[i / 100][1];
				}
			} else if (x > 200 && x < 300) {// 第三格内
				if (s.equals("") || !s.substring(s.length() - 1, s.length()).equals(ss[i / 100][2])) {
					s += ss[i / 100][2];
				}
			} else if (x > 300 && x < 400) {// 第四格内
				if (s.equals("") || !s.substring(s.length() - 1, s.length()).equals(ss[i / 100][3])) {
					s += ss[i / 100][3];
				}
			}

		}
		// 存起来
		if (!s.equals("") && !set.contains(s)) {
			// System.out.println(s);
			count(s);
			set.add(s);
		}

	}

	// 求两侧边结果
	public void getlist4(double x1, double x2) {
		String s = "";
		// 这里x1,x2固定时,y在变化,
		for (int j = 0; j < 400; j++) {
			//每次变化都是不一样的直线
			for (int i = 0; i < j; i++) {
				double x = x2 + (x1 - x2) / j * i;
				if (x > 0 && x < 100) {// 第一格内
					if (s.equals("") || !s.substring(s.length() - 1, s.length()).equals(ss[i / 100][0])) {
						s += ss[i / 100][0];
					}
				} else if (x > 100 && x < 200) {// 第二格内
					if (s.equals("") || !s.substring(s.length() - 1, s.length()).equals(ss[i / 100][1])) {
						s += ss[i / 100][1];
					}
				} else if (x > 200 && x < 300) {// 第三格内
					if (s.equals("") || !s.substring(s.length() - 1, s.length()).equals(ss[i / 100][2])) {
						s += ss[i / 100][2];
					}
				} else if (x > 300 && x < 400) {// 第四格内
					if (s.equals("") || !s.substring(s.length() - 1, s.length()).equals(ss[i / 100][3])) {
						s += ss[i / 100][3];
					}
				}
			}
			// 存起来
			if (!s.equals("") && !set.contains(s)) {
				// System.out.println(s);
				count(s);
				set.add(s);
			}
			s = "";
		}

	}

	public static void main(String[] args) {
		new Main();

	}

}

 

 

 

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值