题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1045
思路一 深度优先搜索
这个应该最容易想到了,从左上开始,按列搜索,到右下结束,具体的搜索顺序如下图
思路二 二分图的最大匹配
一行拆成多行,一列拆成多列
以下图为例
.X..
....
XX..
....
按行排号(左集)
1X22
3333
XX44
5555
按列排号(右集)
1X56
1356
XX56
2456
找到左集和右集中有交叉的情况
左 -> 右
1 -> 1
2 -> 5
2 -> 6
3 -> 1
3 -> 3
3 -> 5
3 -> 6
4 -> 5
4 -> 6
5 -> 2
5 -> 4
5 -> 5
5 -> 6
然后二分匹配,得到最大匹配为
1 -> 1
2 -> 5
3 -> 3
4 -> 6
5 -> 2
则放置碉堡的结果就是
OXO.
.O..
XX.O
O...
程序一 深度优先搜索
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scn = new Scanner(System.in);
while (scn.hasNext()) {
int height = Integer.parseInt(scn.next());
int width = height;
if (0 == height) {
break;
}
Search search = new Search(height, width, 0, 0);
for (int i = 0; i < height; ++i) {
search.setMatrixLine(i, scn.next().toCharArray());
}
search.beginSearch();
System.out.println(search.getMaxNumber());
}
scn.close();
}
}
class Search {
private int height;
private int width;
private char[][] matrix;
private int startX;
private int startY;
private int maxNumber;
public Search(int height, int width, int startX, int startY) {
this.height = height;
this.width = width;
matrix = new char[height][width];
this.startX = startX;
this.startY = startY;
maxNumber = 0;
}
public void beginSearch() {
dfs(startX, startY, 0);
}
private void dfs(int x, int y, int number) {
if (x == height - 1 && y == width - 1) {
if ('.' == matrix[x][y] && isValid(x, y)) {
++number;
}
if (number > maxNumber) {
maxNumber = number;
}
return;
}
if ('X' == matrix[x][y] || !isValid(x, y)) {
if (y < width - 1) {
dfs(x, y + 1, number);
} else {
dfs(x + 1, 0, number);
}
} else {
matrix[x][y] = '*';
if (y < width - 1) {
dfs(x, y + 1, number + 1);
} else {
dfs(x + 1, 0, number + 1);
}
matrix[x][y] = '.';
if (y < width - 1) {
dfs(x, y + 1, number);
} else {
dfs(x + 1, 0, number);
}
}
return;
}
private boolean isValid(int x, int y) {
for (int i = x - 1; i >= 0; --i) {
if ('X' == matrix[i][y]) {
break;
}
if ('*' == matrix[i][y]) {
return false;
}
}
for (int j = y - 1; j >= 0; --j) {
if ('X' == matrix[x][j]) {
break;
}
if ('*' == matrix[x][j]) {
return false;
}
}
return true;
}
public void setMatrixLine(int line, char[] ch) {
matrix[line] = ch;
}
public int getMaxNumber() {
return maxNumber;
}
}
程序二 匈牙利算法 DFS
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scn = new Scanner(System.in);
while (scn.hasNext()) {
int height = Integer.parseInt(scn.next());
int width = height;
if (0 == height) {
break;
}
char[][] city = new char[height][width];
int[][] left = new int[height][width];
for (int i = 0; i < height; ++i) {
city[i] = scn.next().toCharArray();
}
int leftSetNumber = 0;
for (int i = 0; i < height; ++i) { // 按行
if ('.' == city[i][0]) {
left[i][0] = ++leftSetNumber;
} else {
++leftSetNumber;
}
boolean flag = true;
for (int j = 1; j < width; ++j) {
if ('.' == city[i][j]) {
if (flag) {
left[i][j] = leftSetNumber;
} else {
left[i][j] = ++leftSetNumber;
flag = true;
}
} else {
flag = false;
}
}
}
int rightSetNumber = 0;
int[][] right = new int[height][width];
for (int j = 0; j < height; ++j) { // 按列
if ('.' == city[0][j]) {
right[0][j] = ++rightSetNumber;
} else {
++rightSetNumber;
}
boolean flag = true;
for (int i = 1; i < width; ++i) {
if ('.' == city[i][j]) {
if (flag) {
right[i][j] = rightSetNumber;
} else {
right[i][j] = ++rightSetNumber;
flag = true;
}
} else {
flag = false;
}
}
}
Hungary hungary = new Hungary(leftSetNumber, rightSetNumber);
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
if (0 != left[i][j] && 0 != right[i][j]) {
hungary.addEdge(left[i][j] - 1, right[i][j] - 1);
}
}
}
System.out.println(hungary.maxMatch());
}
scn.close();
}
}
class Hungary {
private int leftSetNumber;
private int rightSetNumber;
private boolean[][] matrix;
private int[] leftResult;
private int[] rightResult;
private boolean[] rightUsed;
private boolean searchPath(int left) {
for (int right = 0; right < rightSetNumber; ++right) {
if (matrix[left][right] && !rightUsed[right]) {
rightUsed[right] = true;
if (-1 == rightResult[right] || searchPath(rightResult[right])) {
rightResult[right] = left;
leftResult[left] = right;
return true;
}
}
}
return false;
}
public Hungary(int leftSetNumber, int rightSetNumber) {
this.leftSetNumber = leftSetNumber;
this.rightSetNumber = rightSetNumber;
matrix = new boolean[leftSetNumber][rightSetNumber];
leftResult = new int[leftSetNumber];
rightResult = new int[rightSetNumber];
rightUsed = new boolean[rightSetNumber];
}
public void addEdge(int left, int right) {
matrix[left][right] = true;
}
public int maxMatch() {
Arrays.fill(leftResult, -1);
Arrays.fill(rightResult, -1);
Arrays.fill(rightUsed, false);
int count = 0;
for (int left = 0; left < leftSetNumber; ++left) {
if (-1 == leftResult[left]) {
Arrays.fill(rightUsed, false);
if (searchPath(left)) {
++count;
}
}
}
return count;
}
}
程序三 匈牙利算法 BFS
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scn = new Scanner(System.in);
while (scn.hasNext()) {
int height = Integer.parseInt(scn.next());
int width = height;
if (0 == height) {
break;
}
char[][] city = new char[height][width];
int[][] left = new int[height][width];
for (int i = 0; i < height; ++i) {
city[i] = scn.next().toCharArray();
}
int leftSetNumber = 0;
for (int i = 0; i < height; ++i) { // 按行
if ('.' == city[i][0]) {
left[i][0] = ++leftSetNumber;
} else {
++leftSetNumber;
}
boolean flag = true;
for (int j = 1; j < width; ++j) {
if ('.' == city[i][j]) {
if (flag) {
left[i][j] = leftSetNumber;
} else {
left[i][j] = ++leftSetNumber;
flag = true;
}
} else {
flag = false;
}
}
}
int rightSetNumber = 0;
int[][] right = new int[height][width];
for (int j = 0; j < height; ++j) { // 按列
if ('.' == city[0][j]) {
right[0][j] = ++rightSetNumber;
} else {
++rightSetNumber;
}
boolean flag = true;
for (int i = 1; i < width; ++i) {
if ('.' == city[i][j]) {
if (flag) {
right[i][j] = rightSetNumber;
} else {
right[i][j] = ++rightSetNumber;
flag = true;
}
} else {
flag = false;
}
}
}
Hungary hungary = new Hungary(leftSetNumber, rightSetNumber);
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
if (0 != left[i][j] && 0 != right[i][j]) {
hungary.addEdge(left[i][j] - 1, right[i][j] - 1);
}
}
}
System.out.println(hungary.maxMatch());
}
scn.close();
}
}
class Hungary {
private int leftSetNumber;
private int rightSetNumber;
private boolean[][] matrix;
private int[] leftResult;
private int[] rightResult;
private boolean[] rightUsed;
private int[] preLeft;
public Hungary(int leftSetNumber, int rightSetNumber) {
this.leftSetNumber = leftSetNumber;
this.rightSetNumber = rightSetNumber;
matrix = new boolean[leftSetNumber][rightSetNumber];
leftResult = new int[leftSetNumber];
rightResult = new int[rightSetNumber];
rightUsed = new boolean[rightSetNumber];
preLeft = new int[leftSetNumber];
}
public void addEdge(int left, int right) {
matrix[left][right] = true;
}
public int maxMatch() {
Arrays.fill(leftResult, -1);
Arrays.fill(rightResult, -1);
Arrays.fill(preLeft, -1);
int count = 0;
for (int i = 0; i < leftSetNumber; ++i) {
if (-1 == leftResult[i]) {
Queue<Integer> queue = new LinkedList<Integer>();
queue.offer(i);
boolean hasAP = false;
Arrays.fill(rightUsed, false);
while (!queue.isEmpty() && !hasAP) {
int left = queue.poll();
for (int right = 0; right < rightSetNumber && !hasAP; ++right) {
if (matrix[left][right] && !rightUsed[right]) {
rightUsed[right] = true;
queue.offer(rightResult[right]);
if (0 <= rightResult[right]) {
preLeft[rightResult[right]] = left;
} else {
hasAP = true;
int currentLeft = left;
int currentRight = right;
while (-1 != currentLeft) {
int lastRight = leftResult[currentLeft];
leftResult[currentLeft] = currentRight;
rightResult[currentRight] = currentLeft;
currentLeft = preLeft[currentLeft];
currentRight = lastRight;
}
}
}
}
}
if (-1 != leftResult[i]) {
++count;
}
}
}
return count;
}
}
程序四 Hopcroft-Carp 算法
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scn = new Scanner(System.in);
while (scn.hasNext()) {
int height = Integer.parseInt(scn.next());
int width = height;
if (0 == height) {
break;
}
char[][] city = new char[height][width];
int[][] left = new int[height][width];
for (int i = 0; i < height; ++i) {
city[i] = scn.next().toCharArray();
}
int leftSetNumber = 0;
for (int i = 0; i < height; ++i) { // 按行
if ('.' == city[i][0]) {
left[i][0] = ++leftSetNumber;
} else {
++leftSetNumber;
}
boolean flag = true;
for (int j = 1; j < width; ++j) {
if ('.' == city[i][j]) {
if (flag) {
left[i][j] = leftSetNumber;
} else {
left[i][j] = ++leftSetNumber;
flag = true;
}
} else {
flag = false;
}
}
}
int rightSetNumber = 0;
int[][] right = new int[height][width];
for (int j = 0; j < height; ++j) { // 按列
if ('.' == city[0][j]) {
right[0][j] = ++rightSetNumber;
} else {
++rightSetNumber;
}
boolean flag = true;
for (int i = 1; i < width; ++i) {
if ('.' == city[i][j]) {
if (flag) {
right[i][j] = rightSetNumber;
} else {
right[i][j] = ++rightSetNumber;
flag = true;
}
} else {
flag = false;
}
}
}
HopcroftCarp hopcroftCarp = new HopcroftCarp(leftSetNumber,
rightSetNumber);
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
if (0 != left[i][j] && 0 != right[i][j]) {
hopcroftCarp.addEdge(left[i][j] - 1, right[i][j] - 1);
}
}
}
System.out.println(hopcroftCarp.maxMatch());
}
scn.close();
}
}
class HopcroftCarp {
private final int INF = Integer.MAX_VALUE / 2;
private int leftSetNumber;
private int rightSetNumber;
private boolean[][] matrix;
private int[] leftResult;
private int[] rightResult;
private boolean[] rightUsed;
private int[] leftGradation;
private int[] rightGradation;
private int gradation;
private boolean searchPath() {
Arrays.fill(leftGradation, -1);
Arrays.fill(rightGradation, -1);
gradation = INF;
Queue<Integer> queue = new LinkedList<Integer>();
for (int left = 0; left < leftSetNumber; ++left) {
if (-1 == leftResult[left]) {
queue.offer(left);
++leftGradation[left];
}
}
while (!queue.isEmpty()) {
int left = queue.poll();
if (leftGradation[left] > gradation) {
break;
}
for (int right = 0; right < rightSetNumber; ++right) {
if (matrix[left][right] && -1 == rightGradation[right]) {
rightGradation[right] = leftGradation[left] + 1;
if (-1 == rightResult[right]) {
gradation = rightGradation[right];
} else {
leftGradation[rightResult[right]] = rightGradation[right] + 1;
queue.offer(rightResult[right]);
}
}
}
}
return INF != gradation;
}
private boolean dfs(int left) {
for (int right = 0; right < rightSetNumber; right++) {
if (!rightUsed[right] && matrix[left][right]
&& rightGradation[right] == leftGradation[left] + 1) {
rightUsed[right] = true;
if (-1 != rightResult[right]
&& rightGradation[right] == gradation) {
continue;
}
if (-1 == rightResult[right] || dfs(rightResult[right])) {
rightResult[right] = left;
leftResult[left] = right;
return true;
}
}
}
return false;
}
public HopcroftCarp(int leftSetNumber, int rightSetNumber) {
this.leftSetNumber = leftSetNumber;
this.rightSetNumber = rightSetNumber;
matrix = new boolean[leftSetNumber][rightSetNumber];
leftResult = new int[leftSetNumber];
rightResult = new int[rightSetNumber];
rightUsed = new boolean[rightSetNumber];
leftGradation = new int[leftSetNumber];
rightGradation = new int[rightSetNumber];
gradation = INF;
}
public void addEdge(int left, int right) {
matrix[left][right] = true;
}
public int maxMatch() {
Arrays.fill(leftResult, -1);
Arrays.fill(rightResult, -1);
int count = 0;
while (searchPath()) {
Arrays.fill(rightUsed, false);
for (int left = 0; left < leftSetNumber; ++left) {
if (-1 == leftResult[left] && dfs(left)) {
++count;
}
}
}
return count;
}
}