我必须编写一个程序来读取图片,然后打印出其中的块数。
我必须将图片读作大小为r×c的二进制矩阵(行数乘以数量) 列)。 这些块是一个或多个相邻元素的组,值为1.
INPUT:
在输入的第一行,我们有整数r和c,用一个空格分隔。 然后我们有r行,其中每行包含s 0和1。 各行内的数字不用空格分隔。
OUTPUT仅打印图片中的块数。
例如:
示例1
INPUT:
7 5
01000
00010
00000
10000
01000
00001
00100
输出: 6
示例2:
INPUT:
25 20
00010000000000000000
00010000000000000000
00010000000000000100
00000000000000000100
00011111111000000100
00000000000000000100
00000000000000000100
00000000000000000100
00000000000000000100
01111111111000000100
00000000000000000100
00000000000000100100
00000000000000100100
00000000000000100100
01000000000000100000
01000000000000100000
01000000000000100000
01000000000000100000
00000000000000100000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00011111111111100000
00000000000000000000
输出: 7
问题:
我遇到的问题是我的程序仅适用于例如示例1中的输入。 因此,图片只包含大小为1的块。但如果图片中有多个1,则它不起作用,例如示例2。
在示例2中,输出应为7(块是元素1.它们可以是垂直的或水平的)....我的程序输出是30。
我不知道如何以正确的方式调整程序,因此它会给我正确的输入。
提前感谢您的帮助,这是我发布的代码。
import java.util.Scanner;
class Blocks{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int rowNum=sc.nextInt();
int columnNum=sc.nextInt();
char[][] matrix = new char[rowNum][columnNum];
int nbrOfBlocks = 0;
for (int a = 0; a < rowNum; a++) {
matrix[a] = sc.next().toCharArray();
int index = 0;
while (index < matrix[a].length) {
if (matrix[a][index] == '1') {
++nbrOfBlocks;
while (index < matrix[a].length && matrix[a][index] == '1') {
++index;
}
}
++index;
}
}
System.out.println(nbrOfBlocks);
}
}
答案 0 :(得分:1)
编辑:好的,这是一个适用于复杂形状的解决方案
public class BlockCounter {
public static void main(String[] args) {
Board board = null;
try {
board = new Board("in3.txt");
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
System.out.println("Block count: " + board.getBlockCount());
}
}
class Board {
ArrayList<String> data = new ArrayList<>();
boolean[][] used;
int colCount = 0;
public Board(String filename) throws FileNotFoundException, IOException {
try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
String line;
while ((line = br.readLine()) != null) {
data.add(line);
colCount = Math.max(colCount, line.length());
}
}
}
public int getBlockCount() {
used = new boolean[data.size()][colCount];
int count = 0;
for (int row = 0; row < data.size(); row++)
for (int col = 0; col < colCount; col++)
used[row][col] = peek(row, col) == '1';
for (int row = 0; row < data.size(); row++)
for (int col = 0; col < colCount; col++)
if (used[row][col]) {
fill(row, col);
count++;
}
used = null;
return count;
}
public char peek(int row, int col) {
if (row < 0 || row >= data.size() || col < 0)
return '0';
String rowData = data.get(row);
if (col >= rowData.length())
return '0';
return rowData.charAt(col);
}
public void fill(int row, int col) {
if (used[row][col]) {
used[row][col] = false;
if (row > 0 && used[row - 1][col])
fill(row - 1, col);
if (col > 0 && used[row][col - 1])
fill(row, col - 1);
if (col < colCount - 1 && used[row][col + 1])
fill(row, col + 1);
if (row < data.size() - 1 && used[row + 1][col])
fill(row + 1, col);
}
}
public int getRowCount() {
return data.size();
}
public int getColCount() {
return colCount;
}
}
说明:
当调用Board.getBlockCount()
时,如果创建一个临时的布尔数组,那么原来的板子就不会搞砸了。然后它在整个板上搜索“trues”(对应于板上的'1')。每次找到“true”时,泛洪填充算法都会清除它所连接的整个形状。
如果您需要更大的性能和更少的内存使用(特别是堆栈),您可以使用另一个泛洪填充算法,如下面的示例所示。这里的一大优点是它不会像上面那样使用每个像素的堆栈。但它要复杂得多。
public class BlockCounter2 {
public static void main(String[] args) {
Board2 board = null;
try {
board = new Board2("in4.txt");
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
System.out.println("Block count: " + board.getBlockCount());
}
}
class Board2 {
ArrayList<String> data = new ArrayList<>();
boolean[][] used;
Deque<Point> pointStack = new LinkedList<>();
int colCount = 0;
public Board2(String filename) throws FileNotFoundException, IOException {
try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
String line;
while ((line = br.readLine()) != null) {
data.add(line);
colCount = Math.max(colCount, line.length());
}
}
}
public int getBlockCount() {
used = new boolean[data.size()][colCount];
int count = 0;
for (int row = 0; row < data.size(); row++)
for (int col = 0; col < colCount; col++)
used[row][col] = peek(row, col) == '1';
for (int row = 0; row < data.size(); row++)
for (int col = 0; col < colCount; col++)
if (used[row][col]) {
fill(row, col);
count++;
}
used = null;
return count;
}
public char peek(int row, int col) {
if (row < 0 || row >= data.size() || col < 0)
return '0';
String rowData = data.get(row);
if (col >= rowData.length())
return '0';
return rowData.charAt(col);
}
public void fill(int row, int col) {
pointStack.push(new Point(col, row));
Point p;
while (pointStack.size() > 0) {
p = pointStack.pop();
fillRow(p.y, p.x);
}
}
private void checkRow(int row, int col, int minCol, int maxCol) {
boolean uu = false;
for (int x = col; x < maxCol; x++) {
if (!uu && used[row][x])
pointStack.add(new Point(x, row));
uu = used[row][x];
}
uu = true;
for (int x = col; x > minCol; x--) {
if (!uu && used[row][x])
pointStack.add(new Point(x, row));
uu = used[row][x];
}
}
private void fillRow(int row, int col) {
int lx, rx;
if (used[row][col]) {
for (rx = col; rx < colCount; rx++)
if (used[row][rx])
used[row][rx] = false;
else
break;
for (lx = col - 1; lx >= 0; lx--)
if (used[row][lx])
used[row][lx] = false;
else
break;
if (row > 0)
checkRow(row - 1, col, lx, rx);
if (row < data.size() - 1)
checkRow(row + 1, col, lx, rx);
}
}
public int getRowCount() {
return data.size();
}
public int getColCount() {
return colCount;
}
}
EDIT2:这两个解决方案都是使用txt文件的输入进行的,以便更大规模地调试和测试。如果您需要它们来处理用户输入(您的代码中也是如此),只需进行以下更改:
更改main
方法,以便从用户输入(再次)收听:
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int rowNum=sc.nextInt();
int columnNum=sc.nextInt(); // Note columnNum is not necessary
String[] matrix = new String[rowNum]; // I hope char[][] is not a requirement
for (int a = 0; a < rowNum; a++) // Read array data from user input
matrix[a] = sc.next();
sc.close();
Board2 board = new Board2(matrix); // Call the new constructor
System.out.println("Block count: " + board.getBlockCount());
}
向Board2添加一个新的构造函数,它将String []作为输入:
public Board2(String[] data) {
for (String line : data) {
this.data.add(line);
colCount = Math.max(colCount, line.length());
}
}
如果它对你没用,你可以删除之前的构造函数Board2(String filename)
但是没有必要。
答案 1 :(得分:0)
你在寻找:
import java.util.Scanner;
class Blocks {
public static void removeBlock(char[][] matrix, int posX, int posY) {
if(0 <= posX && posX < matrix.length
&& 0 <= posY && posY < matrix[posX].length) {
if(matrix[posX][posY] == '0') {
return;
}
matrix[posX][posY] = '0';
} else {
return;
}
removeBlock(matrix, posX - 1, posY);
removeBlock(matrix, posX + 1, posY);
removeBlock(matrix, posX, posY - 1);
removeBlock(matrix, posX, posY + 1);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// read in
char[][] matrix = new char[sc.nextInt()][sc.nextInt()];
for(int a = 0; a < matrix.length; a++) {
matrix[a] = sc.next().toCharArray();
}
// calculate number of blocks
int nrBlocks = 0;
for(int i = 0; i < matrix.length; i++) {
for(int j = 0; j < matrix[i].length; j++) {
if(matrix[i][j] == '1') {
// we have found a block, so increment number of blocks
nrBlocks += 1;
// remove any 1's of the block from the array, so that they each block is not counted multiple times
removeBlock(matrix, i, j);
}
}
}
System.out.println(nrBlocks);
}
}
答案 2 :(得分:0)
有一个线性时间(单元格数)解决方案。如果我有时间,我会将代码添加到此答案中,但如果不是,维基百科文章(请参阅下面的编辑)会给出伪代码。
这个想法是逐行扫描,为我们看到的每次1s运行分配递增的唯一运行编号(并将单元格内容更改为该唯一编号)如果在任何运行中,紧接在上面的单元格前一行也是1(也就是说,它们现在有一个运行编号),然后我们知道它们的唯一运行编号和当前唯一运行编号构成了单个块的一部分。所以记录run-above-run-number和current-run-number是等价的(但是不用担心改变板上的任何东西)
最后,我们计算了我们已经看过的次数。和一组唯一运行数的等价关系。对于每组等效数字(比如运行3,5,14形成一个块),我们从运行计数中减去运行次数减1(换句话说,用单个块计数替换多次运行)。 / p>
我认为我有一些想法可以减轻最坏的情况,但他们可能不值得。
那是块的数量。
等价类的最坏情况是O(板的大小)(1宽的垂直块,它们之间有一个空格会这样做)。幸运的是,全黑板只需要O(板的高度) - 每一行都会得到一个数字,它将与下一行标记相同。
编辑:已经有关于此问题的Stackoverflow问题:can counting contiguous regions in a bitmap be improved over O(r * c)?
事实证明,我刚刚重新发明了双通&#34;连接组件标签&#34;在Wikipedia
上讨论的算法