剪格子

题目描述

如图p1.jpg所示,3 x 3 的格子中填写了一些整数。

我们沿着图中的红色线剪开,得到两个部分,每个部分的数字和都是60。

本题的要求就是请你编程判定:对给定的m x n 的格子中的整数,是否可以分割为两个部分,使得这两个区域的数字和相等。

如果存在多种解答,请输出包含左上角格子的那个区域包含的格子的最小数目。

如果无法分割,则输出 0

程序输入输出格式要求:

程序先读入两个整数 m n 用空格分割 (m,n<10)

表示表格的宽度和高度

接下来是n行,每行m个正整数,用空格分开。每个整数不大于10000

程序输出:在所有解中,包含左上角的分割区可能包含的最小的格子数目。

例如:

用户输入:

3 3

10 1 52

20 30 1

1 2 3

则程序输出:

3

再例如:

用户输入:

4 3

1 1 1 1

1 30 80 2

1 1 1 100

则程序输出:

10

(参见p2.jpg)

资源约定:

峰值内存消耗(含虚拟机) < 64M

CPU消耗 < 5000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。

注意:主类的名字必须是:Main,否则按无效代码处理。

import java.util.HashMap;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Stack; /**在蓝桥杯的测试里能AC
* 主要思想分成3部分:
* 1.统计矩阵里所有的数值的情况(这里用TreeMap可能会更好),搜索一个序列,这个序列加起来等于(总和/2 - 左上角的数),即这个序列加左上角的数等于总和的一半
* 2.从左上角开始遍历,测试1得到的数据能不能拼接到一个相连的块中
* 3.测试2得到的结果是不是正好将矩阵分成了两部分
* 采取1的原因在于直接遍历的计算量很大,实际是可能超时的,也可以进行记忆优化,我想大概可以使用HashMap<当前的和, 计算过与否>[10][10]作为搜索的记录
* 步骤1、2都有缺点,都能进一步优化,都能记忆处理
**/
public class Main { static final int MAX = 10005; // 输入值得最大值
static final int MLEN = 105; // 输入矩阵的最大数据个数 static int m, n;
static int[] nums;
static int[] hash;
static int aim; public static void main(String[] args) throws Exception { Scanner sc = new Scanner(System.in);
m = sc.nextInt();
n = sc.nextInt();
nums = new int[n * m];
hash = new int[MAX]; // 散列
uf = new int[m * n]; // 类似并查集,最后判断是否分割成两部分使用 int all = 0; // 总和
for(int i = 0; i < nums.length; i ++) {
int t = sc.nextInt();
nums[i] = t;
hash[t] ++;
all += t;
}
sc.close(); hash[nums[0]] --; // 将位置0从中删除 if(all % 2 != 0) { // 和为奇数
System.out.println(0);
return;
} aim = all / 2 - nums[0]; findAim(0, MAX-1); if(!find)
System.out.println(0); } static int[] list = new int[105]; // 当前已经选择的数值
static int ind = 0; // 列表当前的下标 static boolean find = false; // 当找到了一个合理的答案之后,程序能快速结束 public static void findAim( // 查找一个能满足和为aim的序列
int sum, // 当前的和
int index) { // 遍历的位置 if(find)
return;
if(sum > aim)
return;
if(sum == aim) {
if(isValid()) {
System.out.println(ind + 1);
find = true;
}
return;
} while(index > 0) {
if(hash[index] > 0) {
for(int i = hash[index]; i > -1; i --) { // 加上n个index的值 贪心为上
for(int j = 0; j < i; j ++) { // 更新链表
list[ind ++] = index;
}
findAim(sum + i*index, index-1);
ind -= i; // 回溯
}
return;
}else {
index --;
}
}
} // 判断当前的list是否合法
// 需要判断
// 1.是否能将list数据和arr[0]拼接成一个整体
// 2.判断生成的结果是否可以分成两部分
static boolean isValid() {
validIndex = new HashSet<Integer>();
validIndex.add(1);
validIndex.add(m);
exist[0] = true; // 已经拥有0,0点 // 将list散列化 由于每一次都建立一个MAX大小的向量是不现实的,使用Map结构记录
tempMap.clear();
for(int i = 0; i < ind; i ++) {
Integer old = tempMap.put(list[i], 1); // 当前能够加入的值
if(old != null)
tempMap.put(list[i], 1 + old);
}
return dp(0);
} static HashSet<Integer> validIndex;
static HashMap<Integer, Integer> tempMap = new HashMap<Integer,Integer>(); // 记录list中需要的数据,以及其需要的次数
static boolean[] exist = new boolean[MLEN]; // 当前已经拥有的位置 static boolean dp(int count) {
if(count == ind) {
if(isTwoPart())
return true;
else
return false;
} // 找到当前能加入的点
// 结束时,addStack中包含了当前能加入的点的位置
// addStack里面放的是能加入的位置
Stack<Integer> addStack = new Stack<Integer>();
for(int li:validIndex) { // 遍历当前的合法位置
Integer key = tempMap.get(nums[li]);
if(key != null && key != 0) {
addStack.add(li);
}
} // 选定一个点,加入
while(!addStack.isEmpty()) { int node = addStack.pop();
int type = addNode(node);
if(dp(count + 1))
return true;
removeNode(node, type); } return false;
} public static int addNode(int node) { // 加入一个位置,返回变动的类型
int type = 0;
if((node+1)%m != 0 && !exist[node + 1]) // 如果没到行末
if(validIndex.add(node + 1)) type |= 1;
if((node+m) < m*n && !exist[node + m]) // 如果没到列末
if(validIndex.add(node + m)) type |= 2;
if((node-m) > 0 && !exist[node - m]) // 不是第一行
if(validIndex.add(node - m)) type |= 4;
if(node % m != 0 && !exist[node - 1]) // 不是第一列
if(validIndex.add(node - 1)) type |= 8;
validIndex.remove(node); // 删除自身
exist[node] = true;
tempMap.put(nums[node], tempMap.get(nums[node]) - 1); // 维护map
return type;
} public static void removeNode(int node, int type) {
if((type & 1) != 0) validIndex.remove(node + 1);
if((type & 2) != 0) validIndex.remove(node + m);
if((type & 4) != 0) validIndex.remove(node - m);
if((type & 8) != 0) validIndex.remove(node - 1);
validIndex.add(node);
exist[node] = false;
tempMap.put(nums[node], tempMap.get(nums[node]) + 1); // 维护map
} static int[] uf; // 类似并查集Union Find // 判断是否真的将表格分成两个部分
public static boolean isTwoPart() {
for(int i = 0; i < uf.length; i ++){
uf[i] = -1;
}
// 由于nums[0]一定是被选上的,故,先统计从nums[0]开始能合并多少个节点
uf[0] = 1;
merge(0);
// 找到一个没有被选择的位置,合并
int index = 0;
for(int i = 1; i < exist.length; i ++) {
if(!exist[i]) {
index = i;
break;
}
}
uf[index] = 2;
merge(index); // 经过上述两个合并,使uf中最起码存在两个集合,这两个集合是联通的,且一个被选取的,一个不被选取的
// 如果uf还存在-1,就说明存在不能被两个集合划分
for(int i = 0; i < uf.length; i ++) {
if(uf[i] == -1)
return false;
}
return true;
} // 从index位置开始,将与其相连且标签(标签指是否是否被选取)与其一致的元素合并,考虑exist数组
public static void merge(int index) {
if((index+1)%m != 0 && exist[index] == exist[index+1]) check(index, index + 1);// 如果没到行末,并且行末和这个位置的标签一致
if((index+m) < m*n && exist[index] == exist[index+m]) check(index, index + m);
if((index-m) > 0 && exist[index] == exist[index-m]) check(index, index - m);
if(index % m != 0 && exist[index] == exist[index-1]) check(index, index - 1);
} // 为了能简单merge的代码,抽取代码
// 实际作用是:在已经判明index和next都存在,并且两者标签相同时,应该进行怎样的操作
public static void check(int index, int next) {
if(uf[next] == uf[index]) // 说明是重复搜索
return;
else {
uf[next] = uf[index];
merge(next); // 对next进行搜索
}
} }

java实现第四届蓝桥杯剪格子的更多相关文章

  1. 蓝桥杯---剪格子(DFS&BFS)(小总结)

    问题描述 如下图所示,3 x 3 的格子中填写了一些整数. +--*--+--+ |10* 1|52| +--****--+ |20|30* 1| *******--+ | 1| 2| 3| +--+ ...

  2. java实现第四届蓝桥杯阶乘位数

    阶乘位数 题目描述 如图p1.jpg所示,3 x 3 的格子中填写了一些整数. 我们沿着图中的红色线剪开,得到两个部分,每个部分的数字和都是60. 本题的要求就是请你编程判定:对给定的m x n 的格 ...

  3. 蓝桥杯剪格子dfs

    #include<iostream> #include<cstring> #include<iomanip> #include<cmath> #incl ...

  4. java实现第四届蓝桥杯危险系数

    危险系数 抗日战争时期,冀中平原的地道战曾发挥重要作用. 地道的多个站点间有通道连接,形成了庞大的网络.但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系. 我们来定义一个危险系数DF( ...

  5. java实现第四届蓝桥杯公式求值

    公式求值 输入n, m, k,输出图1所示的公式的值.其中C_n^m是组合数,表示在n个人的集合中选出m个人组成一个集合的方案数.组合数的计算公式如图2所示. 输入的第一行包含一个整数n:第二行包含一 ...

  6. java实现第四届蓝桥杯大臣的旅费

    大臣的旅费 题目描述 很久以前,T王国空前繁荣.为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市. 为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大 ...

  7. java实现第四届蓝桥杯梅森素数

    梅森素数 题目描述 如果一个数字的所有真因子之和等于自身,则称它为"完全数"或"完美数" 例如:6 = 1 + 2 + 3 28 = 1 + 2 + 4 + 7 ...

  8. java实现第四届蓝桥杯连号区间数

    连号区间数 题目描述 小明这些天一直在思考这样一个奇怪而有趣的问题: 在1~N的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是: 如果区间[L, R] 里的所有元素(即此排列的第L个到第R ...

  9. java实现第四届蓝桥杯带分数

    带分数 题目描述 100 可以表示为带分数的形式:100 = 3 + 69258 / 714 还可以表示为:100 = 82 + 3546 / 197 注意特征:带分数中,数字1~9分别出现且只出现一 ...

随机推荐

  1. 简单的Java实现Netty进行通信

    使用Java搭建一个简单的Netty通信例子 看过dubbo源码的同学应该都清楚,使用dubbo协议的底层通信是使用的netty进行交互,而最近看了dubbo的Netty部分后,自己写了个简单的Net ...

  2. java ->IO流_字符流

    字符流 经过前面的学习,我们基本掌握的文件的读写操作,在操作过程中字节流可以操作所有数据,可是当我们操作的文件中有中文字符,并且需要对中文字符做出处理时怎么办呢? 字节流读取字符的问题 通过以下程序读 ...

  3. webpack从零的实践(新手良药)

    1. 什么是webpack? 本质上,webpack是一个现代javascript应用程序的静态模块打包器.webpack处理应用程序时,它会递归地构建一个依赖关系图(dependency graph ...

  4. MySQL数据库基础操作语句

    SQL语言主要用于存取数据.查询数据.更新数据和管理关系数据库系统,分为3种类型: 1.DDL语句 数据库定义语言: 数据库.表.视图.索引.存储过程,例如CREATE DROP ALTER 2.DM ...

  5. 阿里云服务器 ECS Ubuntu系统下PHP,MYSQL,APACHE2的安装配置

    1.系统更新,必须更新,否则有些软件会找不到. apt-get update apt-get upgrade 2.安装mysql sudo apt-get install mysql-server 3 ...

  6. Error creating bean with name 'org.springframework.aop.aspectj.AspectJPointc

    问题 出现报错: Error creating bean with name 'org.springframework.aop.aspectj.AspectJPointc 原因 缺失两个库文件: as ...

  7. 走迷宫(二):在XX限制条件下,是否走得出

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1728 解题方法:BFS+访问数组vis[][]; 给你起点位置和终点位置,让你判断能不能到达,并且拐弯 ...

  8. 想学spark但是没有集群也没有数据?没关系,我来教你白嫖一个!

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是spark专题的第六篇文章,这篇文章会介绍一个免费的spark平台,我们可以基于这个平台做一些学习实验. databricks 今天要 ...

  9. FPGA内部硬件结构简介

    我们知道FPGA内部有很多可供用户任意配置的资源,其中包括:可编程逻辑.可编程I/O.互连线.IP核等资源,很多学过数字电路的人都知道与或非门可以构成几乎所有的数字电路,但是FPGA内部最基本的主要单 ...

  10. linux 去除^M 换行符

    一般,在windows下写的shell脚本,都会去linux执行,都会有^M 符号,那么怎么去除呢? 第一种方法:cat -A filename 就可以看到windows下的断元字符 ^M要去除他,最 ...