提交 38a788cc 编写于 作者: qq_36480062's avatar qq_36480062

c

上级 2eadf9d1
......@@ -29,7 +29,7 @@ public class 迷宫问题 {
}
ArrayDeque<node> q = new ArrayDeque<node>();
int[][] dir = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
q.add(new node(n - 1, n - 1));
q.add(new node(n - 1, n - 1));//终点开始搜
int x = 0, y = 0, a, b;
while (!q.isEmpty()) {
node p = q.poll();
......@@ -43,11 +43,11 @@ public class 迷宫问题 {
if (a == 0 && b == 0) break;
}
}
node p = pre[0][0];
node p = pre[0][0];//搜到起点
while (true) {
System.out.println(p.x + " " + p.y);
if (p.x == n - 1 && p.y == n - 1) break;
p = pre[p.x][p.y];
p = pre[p.x][p.y];//等于它上一个节点
}
}
......
package DFS.剪枝;
import java.util.Arrays;
import java.util.Scanner;
/**
* https://blog.csdn.net/qq_30277239/article/details/105734349
* 搜索做数独
* 数独是一种传统益智游戏,你需要把一个9 × 9的数独补充完整,
* 使得图中每行、每列、每个3 × 3的九宫格内数字1~9均恰好出现一次。
* 请编写一个程序填写数独。
* 输入格式
* 输入包含多组测试用例。
* 每个测试用例占一行,包含81个字符,代表数独的81个格内数据
* (顺序总体由上到下,同行由左到右)。
* 每个字符都是一个数字(1-9)或一个”.”(表示尚未填充)。
* 您可以假设输入中的每个谜题都只有一个解决方案。
* 文件结尾处为包含单词“end”的单行,表示输入结束。
* 输出格式
* 每个测试用例,输出一行数据,代表填充完全后的数独。
* 输入样例:
* 4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......
* ......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
* end
* 输出样例:
* 417369825632158947958724316825437169791586432346912758289643571573291684164875293
* 416837529982465371735129468571298643293746185864351297647913852359682714128574936
* 分析:
* 位运算+剪枝
*/
public class 数独 {
public static void main(String[] args) {
int t = 1 << 10;
for (int i = 0; i < t; i++) {
int j = i;
while (j != 0) {
j -= j & -j;
one[i]++;
}
}
Scanner sc = new Scanner(System.in);
s = sc.next();
while (sc.hasNext()) {
Arrays.fill(g, 0);
Arrays.fill(r, 0);
Arrays.fill(c, 0);
Arrays.fill(w, 0);
s = sc.next();
if (s.equals("end")) {
break;
}
t = 0;
flag = false;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '.') {
t++;
continue;
}
int x = i / 9, y = i % 9;
g[x][y] = s.charAt(i) - '0';
r[x] += 1 << g[x][y];
c[y] += 1 << g[x][y];
int u = get(x, y);
w[u] += 1 << g[x][y];
}
}
}
static int get(int x, int y) {
x /= 3;
y /= 3;
return x * 3 + y;
}
static boolean flag;
static int N = 10, M = 1 << 10 + 1;
static int[] r = new int[N];
static int[] one = new int[M];
static int[] w = new int[N];
static int[] c = new int[N];
static int[][] g = new int[N][N];
static String s;
}
......@@ -6,6 +6,32 @@ import java.util.Scanner;
/**
* https://blog.csdn.net/qq_30277239/article/details/104813141
* 翰翰和达达饲养了N只小猫,这天,小猫们要去爬山。
* 经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_<)。
* 翰翰和达达只好花钱让它们坐索道下山。
* 索道上的缆车最大承重量为W,而N只小猫的重量分别是C1、C2……CN。
* 当然,每辆缆车上的小猫的重量之和不能超过W。
* 每租用一辆缆车,翰翰和达达就要付1美元,所以他们想知道,
* 最少需要付多少美元才能把这N只小猫都运送下山?
* 输入格式
* 第1行:包含两个用空格隔开的整数,N和W。
* 第2..N+1行:每行一个整数,其中第i+1行的整数表示第i只小猫的重量CiCi。
* 输出格式
* 输出一个整数,表示最少需要多少美元,也就是最少需要多少辆缆车。
* 数据范围
* 1≤N≤18,
* 1≤Ci≤W≤10^8
* 输入样例:
* 5 1996
* 1
* 2
* 1994
* 12
* 29
* 输出样例:
* 2
* 分析:
* 本题与上一题都是求满足一定条件下的最小分组数,整体思路一致,只是多了一些剪枝。
*/
public class 运输小猫 {
public static void main(String[] args) {
......@@ -20,9 +46,10 @@ public class 运输小猫 {
public int compare(Integer t2, Integer t1) {
return t1 - t2;
}
});
System.out.println(Arrays.toString(w));
dfs(0, 0);
});//从大到小排序,优先放重的
// dfs(0, 0);
f(0);
System.out.println(ans);
}
......@@ -43,12 +70,39 @@ public class 运输小猫 {
sum[i] -= w[u];
}
}
//k的取值是0~k-1,所以k就是下一辆车
sum[k] = w[u];
dfs(u + 1, k + 1);
sum[k] = 0;
}
/**
* 外部搜索,len是代表第几辆车,
*
* @param u
*/
static void f(int u) {
if (len >= ans) return;
if (u == n) {
ans = len;
System.out.println(Arrays.toString(car));//每辆车放多少个
return;
}
for (int i = 0; i < len; i++) {
if (car[i] + w[u] <= m) {
car[i] += w[u];
f(u + 1);
car[i] -= w[u];
}
}
car[len++] = w[u];
f(u + 1);
car[--len] -= w[u];
}
static int[] car = new int[20];
static Integer[] w = new Integer[20];//记录每只小猫的重量
static int[] sum = new int[20];//每辆车的重量
static int n, m, ans = 20;
static int n, m, ans = 20, len;
}
\ No newline at end of file
......@@ -5,11 +5,57 @@ import java.util.HashMap;
import java.util.Scanner;
/**
* 已知有两个字串 A, B 及一组字串变换的规则(至多6个规则):
* A1 -> B1
* A2 -> B2
* …
* 规则的含义为:在 A 中的子串 A1 可以变换为 B1、A2 可以变换为 B2 …。
* 例如:A=’abcd’ B=’xyz’
* 变换规则为:
* ‘abc’->‘xu’ ‘ud’->‘y’ ‘y’->‘yz’
* 则此时,A 可以经过一系列的变换变为 B,其变换的过程为:
* ‘abcd’->‘xud’->‘xy’->‘xyz’
* 共进行了三次变换,使得 A 变换为B。
* 输入格式
* 输入格式如下:
* A B
* A1 B1
* A2 B2 |-> 变换规则
* … …
* 所有字符串长度的上限为 20。
* 输出格式
* 若在 10 步(包含 10步)以内能将 A 变换为 B ,则输出最少的变换步数;否则输出”NO ANSWER!”
* 输入样例:
* abcd xyz
* abc xu
* ud y
* y yz
* 输出样例:
* 3
* https://blog.csdn.net/qq_30277239/article/details/104723891
* 双向广搜,显然直接bfs会超时
* 每个字符串及其拓展的字符串都可以应用6个规则
* 最坏 20*6=120种扩展
* 最大达到120^10级别
* 本题如果采用普通的BFS求状态转换的最小步数,字符串最长长度是20,
* 假设每一个字符上最多只能进行一种变换,
* 十步以内最多可以进行20^10也就是1024*10^10种状态的搜索,
* 显然这么大的数字会超时或者超空间。即使每个字符串只会做一次变换,
* 也一共要做6^10也就是六千多万种状态的搜索。因为BFS搜索过程中状态的数量是指数级递增的,
* 所以时间增长的特别快。假设从A到B状态一共要做十次转化,C是A到B的一个中间状态,
* 如果A到C要做5次转化,则C到B还要做5次转化。我们同时从A和B进行BFS,
* 各进行五次搜索都搜到C的时候停止,
* 则在两种情况下涉及的状态分别是2 * 20^5=640w以及2 * 6^5 约等于1.54w个状态,
* 显然,这两个状态数都要远小于之前的状态数,或者说只有之前状态数开根号后这么多状态。
* 因此,本题分别从起点和终点同时进行BFS的这种双向BFS的方法能够很好的解决问题。
*
* 需要解决的就是如何同时进行BFS搜索,可以先将开始状态和终点状态分别加入两个不同的队列,
* 如何当两个队列都非空的时候,先对较小的那个队列出队做BFS,以减小状态的增加数量,
* 并且如果两个队头元素的步数之和超过了10,就可以剪枝直接返回了。
* 状态的转换需要考虑从状态x能够转换到哪些状态,因为字符串较短这里直接暴力搜索就可以解决了,
*/
//@SuppressWarnings("all")
public class 串变换 {
public class 串变换 {
public static void main(String[] args) {
String A, B;
Scanner sc = new Scanner(System.in);
......@@ -29,16 +75,16 @@ public class 子串变换 {
}
static int n;
static ArrayDeque<String> qa = new ArrayDeque<String>(), qb = new ArrayDeque<String>();
private static int bfs(String a, String b) {
ArrayDeque<String> qa = new ArrayDeque<String>(), qb = new ArrayDeque<String>();
HashMap<String, Integer> da = new HashMap<String, Integer>();
HashMap<String, Integer> db = new HashMap<String, Integer>();
qa.add(a);
qa.add(a);//起点往终点搜的队列
qb.add(b);
da.put(a, 0);
db.put(b, 0);
//两个数组不能为空
while (!qa.isEmpty() && !qb.isEmpty()) {
int t = 0;
if (qa.size() <= qb.size()) {
......
......@@ -31,6 +31,13 @@ import java.util.Scanner;
* 则要求终点是偶点
* 起点和终点奇偶性相同才可以到达
* 起点(0,0)自然是偶点
* <p>
* 1
* 3 5
* \\/\\
* \\///
* /\\\\
* out:1
*/
public class 电路维修 {
static class node {
......@@ -51,7 +58,8 @@ public class 电路维修 {
for (int i = 0; i < n; i++) {
g[i] = sc.next().toCharArray();
}
if (((n + m) & 1) == 1) System.out.println("NO SOLUTION");
if (((n + m) & 1) == 1) System.out.println("NO SOLUTION");//首先,由于只能斜着走,所以横纵坐标要么同时加1,要么同时减1,
// 要么一个加1一个减1。不论是那种,从起点(0,0)出发,能够到达的点的横纵坐标之和一定是偶数,所以当R + C是奇数时,就无解
else {
bfs(0, 0);
System.out.println(dist[n][m]);
......@@ -61,13 +69,13 @@ public class 电路维修 {
private static int bfs(int x, int y) {
q.clear();
for (boolean[] vi : vis) {
Arrays.fill(vi, false);
}
int t = Integer.MAX_VALUE / 2;
for (int[] ints : dist) {
Arrays.fill(ints, t);
}
for (boolean[] vi : vis) {
Arrays.fill(vi, false);
}
q.add(new node(x, y));
dist[x][y] = 0;
while (!q.isEmpty()) {
......@@ -77,19 +85,20 @@ public class 电路维修 {
for (int i = 0; i < 4; i++) {
int nx = e.x + dir[i][0], ny = e.y + dir[i][1];
if (nx < 0 || nx > n || ny < 0 || ny > m) continue;
int ex = e.x + idx[i][0], ey = e.y + idx[i][1];
int d = dist[e.x][e.y] + (g[ex][ey] != op.charAt(i) ? 1 : 0);
if (d < dist[nx][ny]) {
int ex = e.x + idx[i][0], ey = e.y + idx[i][1];//在格子里的下标
int w = (g[nx][ny] != op.charAt(i) ? 1 : 0);
int d = dist[e.x][e.y] + w;//4个方向,判断这条边有没有,没有就是边权为1的边
if (d < dist[nx][ny]) {//类似于Dijkstra的松弛,能够松弛才入队
dist[nx][ny] = d;
if (g[ex][ey] == op.charAt(i)) q.addFirst(new node(nx, ny));
if (w == 0) q.addFirst(new node(nx, ny));
else q.addLast(new node(nx, ny));
}
}
}
return 0;
return -1;//一定不会被执行到,无解情况已经被奇偶性判掉
}
static String op = "\\/\\/";
static String op = "\\/\\/";//4个方向
static int[][] dir = {{-1, -1}, {-1, 1}, {1, 1}, {1, -1}};
static int[][] idx = {{-1, -1}, {-1, 0}, {0, 0}, {0, -1}};
......
......@@ -3,18 +3,112 @@ package DFS.启发式搜索;
import java.util.Scanner;
/**
*
* https://blog.csdn.net/qq_30277239/article/details/104754393
* 在一个3×3的网格中,1~8这8个数字和一个“X”恰好不重不漏地分布在这3×3的网格中。
* 例如:
* 1 2 3
* X 4 6
* 7 5 8
* 在游戏过程中,可以把“X”与其上、下、左、右四个方向之一的数字交换(如果存在)。
* 我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
* 1 2 3
* 4 5 6
* 7 8 X
* 例如,示例中图形就可以通过让“X”先后与右、下、右三个方向的数字交换成功得到正确排列。
* 交换过程如下:
* 1 2 3 1 2 3 1 2 3 1 2 3
* X 4 6 4 X 6 4 5 6 4 5 6
* 7 5 8 7 5 8 7 X 8 7 8 X
* 把“X”与上下左右方向数字交换的行动记录为“u”、“d”、“l”、“r”。
* 现在,给你一个初始网格,请你通过最少的移动次数,得到正确排列。
* 输入格式
* 输入占一行,将3×3的初始网格描绘出来。
* 例如,如果初始网格如下所示:
* 1 2 3
* x 4 6
* 7 5 8
* 则输入为:1 2 3 x 4 6 7 5 8
* 输出格式
* 输出占一行,包含一个字符串,表示得到正确排列的完整行动记录。如果答案不唯一,输出任意一种合法方案即可。
* 如果不存在解决方案,则输出”unsolvable”。
* 输入样例:
* 2 3 4 1 5 x 7 6 8
* 输出样例
* ullddrurdllurdruldr
*/
public class 八数码 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String start, seq;
String start, seq = "";
StringBuilder s = new StringBuilder();
s.append(sc.nextLine());
start = s.toString().replace(" ", "");
for (int i = 0; i < start.length(); i++) {
if (start.charAt(i) != 'x') seq += start.charAt(i);
}
int cnt = 0;
for (int i = 0; i < 8; i++) {
s.append(sc.nextLine());
for (int j = i; j < 8; j++) {
if (seq.charAt(i) > seq.charAt(j)) {
cnt++;
}
}
}
if ((cnt & 1) == 1) System.out.println("无解");
else {
// System.out.println(bfs());
}
}
// static int bfs(String start) {
// HashMap<String, Integer> dist = new HashMap<String, Integer>();
// HashMap<String, p> prev = new HashMap<String, p>();
// PriorityQueue<node> heap = new PriorityQueue<node>();
// dist.put(start, 0);
// heap.add(new node(f(state), state));
// int[][] dir = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
// while (!heap.isEmpty()) {
// node t = heap.poll();
// String state = t.y;
// if (state == end) break;
// int x, y;
// for (int i = 0; i < 9; i++) {
// if (state.charAt(i) == 'x') {
// x = i / 3;
// y = i % 3;
// break;
// }
// }
// String source = state;
// }
// }
//
// static int f(String state) {
//
// }
static class p {
int x, y;
public p(int x, int y) {
this.x = x;
this.y = y;
}
}
static class node implements Comparable<node> {
int x;
String y;
public node(int x, String y) {
this.x = x;
this.y = y;
}
@Override
public int compareTo(node node) {
return x - node.x;
}
start = s.toString().replace(" ", "");
System.out.println(start);
}
}
......@@ -2,6 +2,7 @@ package DFS.启发式搜索;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Scanner;
/**
* https://www.cnblogs.com/liuwenyao/p/10162462.html
......@@ -30,11 +31,47 @@ import java.util.PriorityQueue;
* 14
* 当终点从pq中第一次取出,那就是最小的,第二次就是,第二小距离,...第n次就是第n小距离
* 证明难证...
* 在上一题八数码问题中,我们详细分析了A*算法的原理和实现。本题同样是A*算法的应用,题目中求的第K短路,要求每条路径至少要包含一条边,所以当起点与终点重合时,要去掉本身这条长度为0的自环边,
* 执行k++操作即可。另外第k短路这里的k可以是重复的排名,
* 比如k = 3时,那么第二短的路径有4条时,求的就是第2短的路径长度了。
* <p>
* 在上一题中,我们证明了A*算法中终点第一次出队时的距离就是最短路径距离,
* 那么是不是终点第k次出队时的距离就是第k短路的距离呢,答案是肯定的,
* 设终点t第二次出队时到起点的距离是g,到终点的估计距离是0(因为要不大于实际距离),
* 队列中任一个顶点离起点的距离是g1,到终点的估计距离是f,因为终点出队了,
* 所以g < g1 + f,又f小于到终点的实际距离d,
* 故g < g1 + d = 经过这个顶点从起点到终点路径的长度,
* 这就证明了当终点t第二次出队时路径长度已经是所有能到达终点路径中最小的了
* (因为终点已经出队过一次了),同理可以证明t第k次出队时的距离就是第k短路距离。
* 因为要求的倍数最短路径长度,所以松弛操作时应该将每次的距离都更新下,不论是否变小,
* 每次被更新时到起点的距离可以直接作为队列的一个属性存入优先级队列。
* <p>
* 下面要考虑如何涉及估价函数,使得每个点的估计距离要小于等于到终点的实际距离,
* 只需要从终点t倒着求一遍dijkstra算法,就可以求出每个点到终点的最短距离了,
* 后面A*算法不论求的是第几短距离到终点的实际距离都不会小于最短距离。题目中的图是有向图,
* 所以不仅需要建立邻接表,还要建立逆邻接表方便倒着来一遍dijkstra。实现细节并不复杂,
* https://www.cnblogs.com/buhuiflydepig/p/11383538.html
*/
public class 第k短路 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
int a, b, c;
for (int i = 0; i < m; i++) {
a = sc.nextInt();
b = sc.nextInt();
c = sc.nextInt();
add(h, a, b, c);
add(rh, b, a, c);//添加反向边
}
S = sc.nextInt();
T = sc.nextInt();
K = sc.nextInt();
if (S == T) K++;
dijkstra();
System.out.println(Astar());
}
static int N = 1010, M = (int) (2e5 + 10), n, m, idx = 1, S, T, K;
......@@ -75,13 +112,14 @@ public class 第k短路 {
}
}
static void add(int a, int b, int c) {
static void add(int[] he, int a, int b, int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
ne[idx] = he[a];
he[a] = idx++;
}
//处理估价函数
static void dijkstra() {
PriorityQueue<node> q = new PriorityQueue<node>();
q.add(new node(0, T));
......@@ -90,9 +128,9 @@ public class 第k短路 {
while (!q.isEmpty()) {
node p = q.poll();
int v = p.to;
if (st[v]) continue;
if (st[v]) continue;//dij
st[v] = true;
for (int i = rh[v]; i != 0; i = ne[i]) {
for (int i = rh[v]; i != 0; i = ne[i]) {//反边
int j = e[i];
if (dist[j] > dist[v] + w[i]) {
dist[j] = dist[v] + w[i];
......@@ -112,10 +150,10 @@ public class 第k短路 {
if (v == T) {
cnt++;
}
if (cnt == K) return dis;
if (cnt == K) return dis;//第k短路
for (int i = h[v]; i != 0; i = ne[i]) {
int j = e[i];
q.add(new tem(dis + w[i] + dist[j], new node(1, 1)));
q.add(new tem(dis + w[i] + dist[j], new node(dis + w[i], j)));
}
}
return -1;
......
......@@ -31,7 +31,7 @@ import static java.lang.System.in;
* 3 2 1 0
* 2 1 0 0
* 1 0 0 1
* 找每个点到指定最近的点
* 找每个点到指定最近的
* 显然:多源最短路,求每个点到一堆起点的距离,终点不唯一找出最近,可以转化成单源最短路
* 有一个虚拟头结点,与所有起点有一条边权为0的边,
* 体现在bfs中就是队列中添加所有起点!!!
......@@ -58,7 +58,7 @@ public class 矩阵距离 {
ArrayDeque<node> q = new ArrayDeque<node>();
for (int i = 0; i < n; i++) {
Arrays.fill(dis[i], -1);
Arrays.fill(dis[i], -1);//dis当做vis数组使用
for (int j = 0; j < m; j++) {
if (g[i][j] == '1') {
q.add(new node(i, j));
......@@ -71,7 +71,7 @@ public class 矩阵距离 {
node p = q.poll();
for (int i = 0; i < 4; i++) {
int x = p.x + dir[i][0], y = dir[i][1] + p.y;
if (x < 0 || x >= n || y < 0 || y >= m || dis[x][y] != -1) continue;
if (x < 0 || x >= n || y < 0 || y >= m || dis[x][y] != -1) continue;//dis当做vis数组使用
dis[x][y] = dis[p.x][p.y] + 1;
q.add(new node(x, y));
}
......
......@@ -4,8 +4,27 @@ import java.util.Scanner;
/**
* https://blog.csdn.net/qq_30277239/article/details/104799213
* 给定 n 个正整数,将它们分组,使得每组中任意两个数互质。
* 至少要分成多少个组?
* 输入格式
* 第一行是一个正整数 n。
* 第二行是 n 个不大于10000的正整数。
* 输出格式
* 一个正整数,即最少需要的组数。
* 数据范围
* 1≤n≤10
* 输入样例:
* 6
* 14 20 33 117 143 175
* 输出样例:
* 3
* 有些数,不能放在一起,求最小组数量,
* 直接暴搜即可。两个数互质,
* 当且仅当这两个数的最大公约数是1,求最大公约数可以用欧几里得算法。
* 至于如何分组,可以考虑以下思路:设已有len个不同分组,遍历到第u个数时,
* 先尝试去加入每个分组,如果与每个分组里所有的数都是互质的,
* 就可以加入。所有分组都尝试过后,再考虑新建一个分组的情况,全部搜完求出len的最小值就是题目所求的最优解。
* 这种见另一种写法
*/
public class 分成互质组 {
public static void main(String[] args) {
......@@ -18,6 +37,12 @@ public class 分成互质组 {
System.out.println(ans);
}
/**
* @param g 某一组的所有元素,数组中存的是质数的下标
* @param gc 某一组的最多元素
* @param i 应当放置数组的下标
* @return 把第i个数能否放进第g这个组
*/
static boolean check(int[] g, int gc, int i) {
for (int j = 0; j < gc; j++) {
if (gcd(a[g[j]], a[i]) > 1) return false;
......@@ -26,9 +51,24 @@ public class 分成互质组 {
}
//g代表当前哪一组,gc代表枚举到组内第几个元素tc表示当前一共处理多少数,start代表组内从哪个数开始枚举
/**
* 只有2个动作
* 1:把当前数加到最后一组
* 2:当前数加到新开的组
* 假设:当前数与之前组的数字互质,不考虑
* 若能将当前数,放到最后一组的末尾,而不冲突,但我们开新的组是最优解,
* 则显然,放末尾不影响最优解,
* 所以优先度最高放最后一个组的末尾
*
* @param g g代表当前哪一组,组编号
* @param gc gc代表枚举到组内第几个元素
* @param tc tc表示当前一共处理多少数
* @param start start代表组内从哪个数开始枚举,组合数搜索!
*/
static void dfs(int g, int gc, int tc, int start) {
if (g >= ans) return;
if (tc == n) {
if (g >= ans) return;//最优性剪枝
if (tc == n) {//这个时候g<ans,因为g>=ans被判掉了
ans = g;
}
boolean f = true;
......@@ -37,17 +77,18 @@ public class 分成互质组 {
vis[i] = true;
group[g][gc] = i;
dfs(g, gc + 1, tc + 1, i + 1);
//放在当前组的末尾
vis[i] = false;
f = false;
}
}
if (f) dfs(g + 1, 0, tc, 0);
if (f) dfs(g + 1, 0, tc, 0);//开新的组,
}
static int n, ans = 10;
static int[] a = new int[10];
static int[][] group = new int[10][10];
static boolean[] vis = new boolean[10];
static int n, ans = 100;
static int[] a = new int[100];
static int[][] group = new int[100][100];
static boolean[] vis = new boolean[100];
static int gcd(int a, int b) {
if (b == 0) return a;
......
package DFS.搜索顺序;
import java.util.ArrayList;
import java.util.Scanner;
public class 分成互质组另一种写法 {
public static void main(String[] args) {
for (int i = 0; i <= 10; i++) {
group[i] = new ArrayList<Integer>();
}
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
dfs(0);
System.out.println(ans);
}
/**
* 设已有len个不同分组,遍历到第u个数时,
* 先尝试去加入每个分组,如果与每个分组里所有的数都是互质的,
* 就可以加入。所有分组都尝试过后,再考虑新建一个分组的情况,
* 全部搜完求出len的最小值就是题目所求的最优解。
* 每个数有两个抉择:
* 1:如果不冲突把当前数,放进前面几个组中去
* 2:第一种不行就新开组,
*
* @param u
*/
static void dfs(int u) {
if (u == n) {
ans = Math.min(ans, len);
return;
}
for (int i = 0; i < len; i++) {
if (check(a[u], i)) {//枚举把第i个数,放进第i组
group[i].add(a[u]);
dfs(u + 1);
group[i].remove(group[i].size() - 1);//回溯删掉最后一个
}
}
group[len++].add(a[u]);//开新的组
dfs(u + 1);
group[--len].remove(group[len].size() - 1);//回溯删掉最后一个
}
static boolean check(int x, int t) {
for (int i = 0; i < group[t].size(); i++) {
if (gcd(x, group[t].get(i)) > 1) return false;
}
return true;
}
static ArrayList<Integer>[] group = new ArrayList[12];
static int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
static int n, ans = 12, len;
static int[] a = new int[12];
}
package DFS.搜索顺序;
import java.util.ArrayList;
import java.util.Scanner;
/**
* n个人参加某项特殊考试。
* 为了公平,要求任何两个认识的人不能分在同一个考场。
* 求是少需要分几个考场才能满足条件。
* 输入
* 第一行,一个整数n(1<n<100),表示参加考试的人数。
* 第二行,一个整数m,表示接下来有m行数据
* 以下m行每行的格式为:两个整数a,b,用空格分开 (1<=a,b<=n) 表示第a个人与第b个人认识。
* 输出
* 一行一个整数,表示最少分几个考场。
* 5
* 8
* 1 2
* 1 3
* 1 4
* 2 3
* 2 4
* 2 5
* 3 4
* 4 5
* out:4
*/
public class 分考场 {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
n = s.nextInt();
m = s.nextInt();
int a, b;
for (int i = 0; i < m; i++) {
a = s.nextInt();
b = s.nextInt();
p[a][b] = p[b][a] = 1;
}
dfs(1);
System.out.println(ans);
}
static void dfs(int u) {
if (list.size() >= ans) return;//最优性剪枝
if (u > n) {
ans = Math.min(ans, list.size());
return;
}
for (int i = 0; i < list.size(); i++) {
if (check(i, u)) {
list.get(i).add(u);
dfs(u + 1);
list.get(i).remove(list.get(i).size() - 1);//回溯
}
}
ArrayList<Integer> q = new ArrayList<Integer>();
q.add(u);
list.add(q);
dfs(u + 1);
list.remove(list.size() - 1);
}
/**
* @param i 第i组
* @param u 当前数
* @return 能否把u放进第i组
*/
private static boolean check(int i, int u) {
for (int j = 0; j < list.get(i).size(); j++) {
if (p[list.get(i).get(j)][u] == 1) return false;
}
return true;
}
static int ans = 100;
static int[][] p = new int[310][310];//每一组,放多少
static ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();
static int n, m;
}
......@@ -2,8 +2,34 @@ package DFS.搜索顺序;
import java.util.Scanner;
import static java.lang.Math.max;
/**
*https://blog.csdn.net/qq_30277239/article/details/104782198
* https://blog.csdn.net/qq_30277239/article/details/104782198
* 单词接龙是一个与我们经常玩的成语接龙相类似的游戏。
* 现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”,每个单词最多被使用两次。
* 在两个单词相连时,其重合部分合为一部分,例如 beast 和 astonish ,如果接成一条龙则变为 beastonish。
* 我们可以任意选择重合部分的长度,但其长度必须大于等于1,且严格小于两个串的长度,例如 at 和 atide 间不能相连。
* 输入格式
* 输入的第一行为一个单独的整数 n 表示单词数,
* 以下 n 行每行有一个单词(只含有大写或小写字母,长度不超过20),输入的最后一行为一个单个字符,表示“龙”开头的字母。
* 你可以假定以此字母开头的“龙”一定存在。
* 输出格式
* 只需输出以此字母开头的最长的“龙”的长度。
* 数据范围
* n≤20
* 输入样例:
* 5
* at
* touch
* cheat
* choose
* tact
* a
* 输出样例:
* 23
* 提示
* 连成的“龙”为 atoucheatactactouchoose。
*/
public class 单词接龙 {
public static void main(String[] args) {
......@@ -13,33 +39,70 @@ public class 单词接龙 {
word[i] = sc.next();
}
char start = sc.next().charAt(0);
//预处理,g[i][j]表示第i个单词的后缀和第j个单词的前缀的最大匹配
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
String a = word[i], b = word[j];
for (int k = 1; k < Math.min(a.length(), b.length()); k++) {
if (a.substring(a.length() - k, k).equals(b.substring(0, k))) {
//a的后缀,和b的前缀匹配
g[i][j] = k;
if (a.substring(a.length() - k).equals(b.substring(0, k))) {
g[i][j] = k;//a的后缀,和b的前缀相匹配的最短长度
break;
}
}
}
}
// for (int i = 0; i < n; i++) {
// for (int j = 0; j < n; j++) {
// int p = word[i].length();
// int q = word[j].length();
// for (int k = 1; k < p && k < q; k++) {
// if (word[i].substring(p - k).equals(word[j].substring(0, k))) {
// g[i][j] = k;
// break;
// }
// }
// }
// }
for (int i = 0; i < n; i++) {
if (word[i].charAt(0) == start) {
dfs(word[i], i);
// ans = max(dfs(i), ans);
}
}
System.out.println(ans);
}
/**
* @param u 记录上一个单词
* @return
*/
static int dfs(int u) {
int res = 0;
cnt[u]++;
for (int i = 0; i < n; i++) {
if (cnt[i] >= 2) continue;
if (g[u][i] > 0) res = max(res, dfs(i) - g[u][i]);
}
cnt[u]--;
return res + word[u].length();
}
static int[] cnt = new int[30];
/**
* @param s 当前拼接单词
* @param last 上一个单词
*/
static void dfs(String s, int last) {
ans = Math.max(s.length(), ans);
vis[last]++;
for (int i = 0; i < n; i++) {
if (g[last][i] != 0 && vis[i] < 2) {
if (g[last][i] > 0 && vis[i] < 2) {
dfs(s + word[i].substring(g[last][i]), i);
}
}
vis[last]--;
}
static int n, N = 21, ans;
......
......@@ -42,9 +42,10 @@ public class 魔板 {
}
String st = start.toString();
String ed = end.toString();
bfs(st, ed);
if (st.equals(ed)) System.out.println(0);
else {
bfs(st, ed);
System.out.println(dist.get(ed));
StringBuilder res = new StringBuilder();
while (!ed.equals(st)) {
......@@ -58,6 +59,10 @@ public class 魔板 {
}
}
static Map<String, Integer> dist = new HashMap<String, Integer>();//记录步数
static Map<String, node> pre = new HashMap<String, node>();//记录路径
static ArrayDeque<String> q = new ArrayDeque<String>();
private static void bfs(String start, String end) {
if (start.equals(end)) return;
q.add(start);
......@@ -136,7 +141,4 @@ public class 魔板 {
return get();
}
static Map<String, Integer> dist = new HashMap<String, Integer>();
static Map<String, node> pre = new HashMap<String, node>();
static ArrayDeque<String> q = new ArrayDeque<String>();
}
......@@ -7,7 +7,7 @@ import java.util.Scanner;
/**
* https://blog.csdn.net/qq_30277239/article/details/104680308
* bfs
* 显然边界问题
* 考虑边界问题
* n*2不会超过20w+10
* n-1不会低于0
* 如果越界则无需记录
......@@ -21,6 +21,7 @@ public class 抓住那头牛 {
Arrays.fill(d, -1);
d[n] = 0;
q.add(n);
//bfs
while (!q.isEmpty()) {
int t = q.poll();
if (t == k) {
......
......@@ -23,7 +23,7 @@ public class 马走日 {
Arrays.fill(vis[i], false);
}
vis[x][y] = true;
dfs(x, y, 1);
dfs(x, y, 1);//起点本身可以到达
System.out.println(ans);
}
}
......
package DFS.连通性;
import java.util.Scanner;
/**
* 有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。
* 你站在其中一块黑色的瓷砖上,只能向相邻(上下左右四个方向)的黑色瓷砖移动。
* 请写一个程序,计算你总共能够到达多少块黑色的瓷砖。
* 输入格式
* 输入包括多个数据集合。
* 每个数据集合的第一行是两个整数 W 和 H,分别表示 x 方向和 y 方向瓷砖的数量。
* 在接下来的 H 行中,每行包括 W 个字符。每个字符表示一块瓷砖的颜色,规则如下
* 1)‘.’:黑色的瓷砖;
* 2)‘#’:白色的瓷砖;
* 3)‘@’:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合中唯一出现一次。
* 当在一行中读入的是两个零时,表示输入结束。
* 输出格式
* 对每个数据集合,分别输出一行,显示你从初始位置出发能到达的瓷砖数(记数时包括初始位置的瓷砖)。
* 数据范围
* 1≤W,H≤20
* 输入样例:
* 6 9
* ....#.
* .....#
* ......
* ......
* ......
* ......
* ......
* #@...#
* .#..#.
* 0 0
* 输出样例:
* 45
* 分析:
* 本题是求连通块的大小,数据量不大,可以用dfs实现,由于比较简单,
*/
public class 红与黑 {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
n = s.nextInt();
m = s.nextInt();
for (int i = 0; i < m; i++) {
g[i] = s.next().toCharArray();
}
sx = s.nextInt();
sy = s.nextInt();
System.out.println(dfs(sx, sy));
}
static int dfs(int sx, int sy) {
if (g[sx][sy] == '#') return 0;
int cnt = 1; //dfs在开头判掉,bfs在内部判掉
st[sx][sy] = true;
for (int i = 0; i < 4; i++) {
int nx = sx + dir[i][0], ny = sy + dir[i][1];
if (nx >= 0 && nx < m && ny >= 0 && ny < n && !st[nx][ny]) {
cnt += dfs(nx, ny);
}
}
return cnt;
}
static int[][] dir = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
static boolean[][] st = new boolean[22][22];
static int n, m, sx, sy;
static char[][] g = new char[22][22];
}
package DFS.连通性;
import java.util.Arrays;
import java.util.Scanner;
/**
* 一天Extense在森林里探险的时候不小心走入了一个迷宫,迷宫可以看成是由 n∗n的格点组成,每个格点只有2种状态,.和#,前者表示可以通行后者表示不能通行。
* 同时当Extense处在某个格点时,他只能移动到东南西北(或者说上下左右)四个方向之一的相邻格点上,Extense想要从点A走到点B,问在不走出迷宫的情况下能不能办到。
* 如果起点或者终点有一个不能通行(为#),则看成无法办到。
* 注意:A、B不一定是两个不同的点。
* 输入格式
* 第1行是测试数据的组数 k,后面跟着 k 组输入。
* 每组测试数据的第1行是一个正整数 n,表示迷宫的规模是 n∗n 的。
* 接下来是一个 n∗n 的矩阵,矩阵中的元素为.或者#。
* 再接下来一行是 4 个整数 ha,la,hb,lb,描述 AA 处在第 ha 行, 第 la 列,BB 处在第 hb 行, 第 lb 列。
* 注意到 ha,la,hb,lb全部是从 0 开始计数的。
* 输出格式
* k行,每行输出对应一个输入。
* 能办到则输出“YES”,否则输出“NO”。
* 数据范围
* 1≤n≤100
* 输入样例:
* 2
* 3
* .##
* ..#
* #..
* 0 0 2 2
* 5
* .....
* ###.#
* ..#..
* ###..
* ...#.
* 0 0 4 0
* 输出样例:
* YES
* NO
* 分析:
* 本题同样是Flood Fill问题,不过只用求连通性而不用求最短距离,
* 这时DFS更加有优势,只要不爆栈,DFS可以一直深入直到走到终点,而BFS差不多要遍历完所有的状态,
* 比较不划算。本题要注意后台数据的起点和终点位置都可能有障碍物,这时候就是不连通的了;
* 另外,已经走过的点必须要置为已访问,下次遇见不再扩展,不然重复搜索效率极低。
*/
public class 迷宫 {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
t = s.nextInt();
while (t-- != 0) {
for (int i = 0; i <= 100; i++) {
Arrays.fill(st[i], false);
}
n = s.nextInt();
for (int i = 0; i < n; i++) {
map[i] = s.next().toCharArray();
}
int sx = s.nextInt();
int sy = s.nextInt();
tx = s.nextInt();
ty = s.nextInt();
if (dfs(sx, sy)) System.out.println("YES");
else System.out.println("NO");
}
}
static int tx, ty;
//是否能到达,不要求最短距离
static boolean dfs(int sx, int sy) {
if (map[sx][sy] == '#') return false;
if (sx == tx && sy == ty) return true;
st[sx][sy] = true;
//dfs在开头判掉,bfs在内部判掉
for (int i = 0; i < 4; i++) {
int nx = dir[i][0] + sx, ny = sy + dir[i][1];
if (nx >= 0 && nx < n && ny >= 0 && ny < n && !st[nx][ny]) {
st[nx][ny] = true;
if (dfs(nx, ny)) return true;
}
}
return false;
}
static int[][] dir = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
static boolean[][] st = new boolean[110][110];
static int n, t;
static char[][] map = new char[110][110];
}
package graph;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
public class KruskalByEdge {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
n = s.nextInt();
m = s.nextInt();
for (int i = 1; i < 1e5 + 7; i++) {
p[i] = i;
}
for (int i = 0; i < m; i++) {
q.add(new edge(s.nextInt(), s.nextInt(), s.nextInt()));
}
int res = 0;
int cnt = 0;
Collections.sort(q);
for (int i = 0; i < q.size(); i++) {
edge e = q.get(i);
int a = find(e.x), b = find(e.y), c = e.z;
if (a != b) {
p[a] = b;
res += c;
cnt++;
}
}
if (cnt != n - 1) System.out.println("INF");
System.out.println(res);
}
static int find(int x) {
if (x == p[x]) return x;
return p[x] = find(p[x]);
}
static ArrayList<edge> q = new ArrayList<edge>();
static int[] p = new int[(int) (1e5 + 10)];
static class edge implements Comparable<edge> {
int x, y, z;
public edge(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
@Override
public int compareTo(edge edge) {
return z - edge.z;
}
}
static int n, m;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册