初级DFS练习:POJ1111 POJ1129 POJ2245
POJ1111
[题目链接]( 1111 -- Image Perimeters (poj.org) )
详细代码:
package poj1111;
import java.util.Scanner;
/**
* @Author jinjun99
* @Date Created in 2021/11/27 9:49
* @Description 题目大意:二维数组表示一张X光照片的像素点,
* 一个对象的图案对应的像素点用X表示,其他空间用.表示
* 鼠标点击一个对象中的某个像素点X时,会选中该对象
* 要求算出该对象的边缘周长。
* 解题思路:用DFS遍历选中对象所有的像素点,
* 碰到上下左右四个方向的边界时周长+1。
* @Since version-1.0
*/
public class Main {
/**
* 行
*/
static int r;
/**
* 列
*/
static int c;
/**
* 初始坐标
*/
static int x;
static int y;
/**
* 图像数组
*/
static char[][] img;
/**
* 标记数组
*/
static int[][] mark;
/**
* 周长
*/
static int perimeter;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()){
String str = sc.nextLine();
String[] s = str.split(" ");
r = Integer.parseInt(s[0]);
c = Integer.parseInt(s[1]);
x = Integer.parseInt(s[2])-1;
y = Integer.parseInt(s[3])-1;
if (r==0 && c==0 && x==-1 && y==-1) {
break;
}
img = new char[r][c];
mark = new int[r][c];
for (int i = 0; i < r; i++) {
String s1 = sc.nextLine();
char[] c1 = s1.toCharArray();
for (int j = 0; j < c; j++) {
img[i][j] = c1[j];
}
}
perimeter = 0;
dfs(x,y);
System.out.println(perimeter);
}
}
/**
* 方向数组
*/
private static int[] dirX = {0,1,0,-1,-1,1,-1,1};
private static int[] dirY = {1,0,-1,0,-1,1,1,-1};
private static void dfs(int x, int y) {
/*dfs所有'X'*/
if (img[x][y]=='X'&&mark[x][y]==0){
mark[x][y]=1;
/*8个前进搜索方向*/
for (int i = 0; i < 8; i++) {
int a = x+dirX[i];
int b = y+dirY[i];
boolean bl1 = a>=0&&b>=0&&a<r&&b<c;
/*如果出界或者是'.',则当前子树结束搜索*/
if (!bl1 || img[a][b] == '.'){
/*如果是前4个方向出界,则累计一个单位的边长*/
if (i<4){
perimeter++;
}
}else {
dfs(a,b);
}
}
}
}
}
/*
输入:
2 2 2 2
XX
XX
6 4 2 3
.XXX
.XXX
.XXX
...X
..X.
X...
5 6 1 3
.XXXX.
X....X
..XX.X
.X...X
..XXX.
7 7 2 6
XXXXXXX
XX...XX
X..X..X
X..X...
X..X..X
X.....X
XXXXXXX
7 7 4 4
XXXXXXX
XX...XX
X..X..X
X..X...
X..X..X
X.....X
XXXXXXX
0 0 0 0
输出:
8
18
40
48
8
*/
POJ1129
[题目链接]( 1129 -- Channel Allocation (poj.org) )
详细代码:
package poj1129;
import java.util.Scanner;
/**
* @Author jinjun99
* @Date Created in 2021/11/25 17:49
* @Description 题目大意:广播需要使用多个中继器扩大范围,相邻的中继器不能使用相同的频道以免干扰
* 给出一组中继器网络,标记出该网络中每个中继器分别和哪些中继器相邻。问最少需要几个频道才能互不干扰。
* 解题思路:四色问题,频道数范围是2-4,用DFS依次搜索2-4个频道下是否干扰即可。
* @Since version-1.0
*/
public class Main {
/**
* 中继器数量
*/
static int n;
/**
* 中继器地图数组
*/
static int[][] map;
/**
* 频道分配数组
*/
static int[] distribute;
/**
* 用来记录频道数为2,3,4时分别的分配方案数
*/
static int[] scheme = new int[3];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
/*获取第一个数字,即中继器数量*/
n = sc.nextInt();
/*如果数量为0,则结束*/
if (n == 0) {
break;
}
/*初始化中继器地图数组和频道数组*/
map = new int[n][n];
distribute = new int[n];
scheme = new int[3];
/*是否有相邻的边*/
boolean side = false;
/*接收一组数据输入,给数组赋值*/
for (int i = 0; i < n; i++) {
String str = sc.next();
/*每个中继器按字母表顺序用字母表示,可以用第i个字母-第一个
字母A,利用ASCII码之差得到对应字母在字母表中的序数,*/
int charNum = str.charAt(0) - 'A';
/*获取第一个字母表示的中继器相邻的其他中继器*/
/*跳过冒号从2开始*/
for (int j = 2; j < str.length(); j++) {
/*获取字母对应的序数*/
int adja = str.charAt(j) - 'A';
map[charNum][adja] = 1;
map[adja][charNum] = 1;
side = true;
}
}
/*频道数*/
int channel = 1;
if (side) {
dfs(0, 0, distribute);
for (int i = 0; i < 3; i++) {
if (scheme[i] != 0) {
channel = i + 2;
System.out.println(channel + " channels needed.");
break;
}
}
} else {
System.out.println("1 channel needed.");
}
}
}
/**
* @param r 当前中继器编号
* @param c 当前子树能使用的频道数
* @param d 当前的频道安排情况
* @return
*/
private static void dfs(int r, int c, int[] d) {
if (r == 0) {
for (int i = 2; i <= 4; i++) {
d = new int[n];
dfs(1, i, d);
}
} else {
/*当4个中继器遍历完*/
if (r == n + 1) {
/*对应限制c个频道的方案数+1*/
scheme[c - 2]++;
return;
}
/*遍历当前能使用的频道代号*/
for (int i = 1; i <= c; i++) {
/*对第r个中继器安排i频道*/
d[r - 1] = i;
/*判断条件*/
if (satisfied(r, c, d)) {
dfs(r + 1, c, d);
}
/*回溯省略,下一层循环自动刷新*/
}
}
}
/**
* @param r 当前中继器编号
* @param c 当前子树能使用的频道数
* @param d 当前的频道安排情况
* @return
*/
private static boolean satisfied(int r, int c, int[] d) {
/*剪枝条件,如果当前限制的频道数(或前一个限制)还未找到一个符合条件的方案*/
boolean condition = (c == 2 && scheme[c - 2] == 0) || (c > 2 && scheme[c - 2] == 0 && scheme[c - 3] == 0);
if (condition) {
/*遍历地图找到当前中继器邻接的其他中继器,依次对比是否符合条件*/
for (int i = 0; i < n; i++) {
/*相邻的中继器频道不同*/
if (map[r - 1][i] == 1 && d[r - 1] == d[i]) {
return false;
}
}
return true;
}
return false;
}
}
/*
输入:
2
A:
B:
4
A:BC
B:ACD
C:ABD
D:BC
4
A:BCD
B:ACD
C:ABD
D:ABC
0
输出:
1 channel needed.
3 channels needed.
4 channels needed.
*/
POJ2245
[题目链接]( 2245 -- Lotto (poj.org) )
详细代码:
package poj2245;
import java.util.ArrayList;
import java.util.Scanner;
/**
* @Author jinjun99
* @Date Created in 2021/11/26 12:52
* @Description 题目大意:在德国乐透中你需要从一个集合[1,2..49]中选中6个数字,
* 一种流行的策略是先从原始集合中选出k(k>6)个数字的子集S,再从子集中选择6个数字
* 输入包含一个k和对应的子集S,要求按字典序打印从S中选6个数的所有可能。
* 解题思路:其实就是求组合C(n=k,m=6),字典序就是限制每一位数比前一位大
* @Since version-1.0
*/
public class Main {
/**
* 集合长度
*/
static int k;
/**
* 集合
*/
static int[] set;
/**
* 固定取6个数
*/
static int m = 6;
/**
* 所有子集
*/
static ArrayList<int[]> sub;
/**
* 当前子集
*/
static int[] currSub;
/**
* 被选过的数
*/
static boolean[] selected;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// long st = System.currentTimeMillis();
while (sc.hasNext()){
/*数据输入和初始化*/
String str = sc.nextLine();
String[] s1 = str.split(" ");
k = Integer.parseInt(s1[0]);
if (k==0){
break;
}
set = new int[k];
currSub = new int[m];
selected = new boolean[k];
sub = new ArrayList<int[]>();
for (int i = 0; i < k; i++) {
set[i] = Integer.parseInt(s1[i+1]);
}
/*搜索*/
dfs(0);
/*打印*/
for (int i = 0; i < sub.size(); i++) {
int[] subSetI = sub.get(i);
for (int j = 0; j < m; j++) {
if (j==m-1){
System.out.println(subSetI[j]);
}else {
System.out.print(subSetI[j]+" ");
}
}
}
System.out.println("");
}
// long e = System.currentTimeMillis();
// System.out.println("计算时长:"+(e-st)+"ms");
}
/**
* @param l 解空间树的层数,当前子集的第几个数
*/
private static void dfs(int l) {
if (l==m){
/*到达叶子节点,放入子集集合中*/
int[] sub1 = new int[m];
System.arraycopy(currSub, 0, sub1, 0, m);
sub.add(sub1);
return;
}
/*遍历k个数*/
for (int i = 0; i < k; i++) {
/*剪枝:第一个数的下标不能大于k-m,否则后面选出来的都不符合字典序*/
if (l==0&&k-i<6){
return;
}
/*当前set[i]没被选过并且大于当前子集的前一个数(字典序)*/
boolean bl = !selected[i]&&(l==0||(l>0&&set[i]>currSub[l-1]));
if (bl){
/*填入当前子集,并锁定set中的当前数字*/
currSub[l]=set[i];
selected[i]=true;
dfs(l+1);
/*回溯*/
selected[i]=false;
currSub[l]=0;
}
}
}
}
/*
输入:
7 1 2 3 4 5 6 7
8 1 2 3 5 8 13 21 34
0
输出:
1 2 3 4 5 6
1 2 3 4 5 7
1 2 3 4 6 7
1 2 3 5 6 7
1 2 4 5 6 7
1 3 4 5 6 7
2 3 4 5 6 7
1 2 3 5 8 13
1 2 3 5 8 21
1 2 3 5 8 34
1 2 3 5 13 21
1 2 3 5 13 34
1 2 3 5 21 34
1 2 3 8 13 21
1 2 3 8 13 34
1 2 3 8 21 34
1 2 3 13 21 34
1 2 5 8 13 21
1 2 5 8 13 34
1 2 5 8 21 34
1 2 5 13 21 34
1 2 8 13 21 34
1 3 5 8 13 21
1 3 5 8 13 34
1 3 5 8 21 34
1 3 5 13 21 34
1 3 8 13 21 34
1 5 8 13 21 34
2 3 5 8 13 21
2 3 5 8 13 34
2 3 5 8 21 34
2 3 5 13 21 34
2 3 8 13 21 34
2 5 8 13 21 34
3 5 8 13 21 34
*/