剪枝
1.优化搜索顺序:在大部分情况下,我们应该优先搜索分支较少的结点
2.排除等效冗余(在不考虑顺序的情况下,尽量用组合的方式来搜索)
3.可行性剪枝
4.最优性剪枝
5.记忆化搜索
- import java.util.*;
-
- public class Main{
- static int N = 20;
- static int n, m, res = N;
- static int[] w = new int[N];//每只小猫的数量
- static int[] sum = new int[N];//每个缆车已经放的小猫的重量之和
-
- public static void dfs(int u, int k){//u猫的数量,共有k个缆车
- //最优性剪枝
- if(k >= res) return;
- if(u == n){
- res = k;
- return;
- }
-
- //能在以前的车里面找到位置
- for(int i = 0; i < k; i ++){
- if(sum[i] + w[u] <= m){//可行性剪枝
- sum[i] += w[u];
- dfs(u + 1, k);
- sum[i] -= w[u];//回溯,恢复现场
- }
- }
-
- //新开一辆车
- sum[k] = w[u];//数组是从0开始的,所以这里的第k+1辆车用k表示
- dfs(u + 1, k + 1);
- sum[k] = 0;//恢复现场
- }
-
- public static void main(String[] args){
- Scanner sc = new Scanner(System.in);
- n = sc.nextInt();
- m = sc.nextInt();
- for(int i = 0; i < n; i ++){
- w[i] = sc.nextInt();
- }
-
- //优化搜索顺序,分支少的先搜索(重猫)
- Arrays.sort(w, 0, n);//排序
- int[] b = new int[n];
- for(int i = n - 1, j = 0; i >= 0; i --){
- b[j ++] = w[i];
- }
- for(int i = 0; i < n; i ++) w[i] = b[i];
-
- dfs(0, 0);
-
- System.out.print(res);
- }
- }
可以用一个九位的二进制数来表示一行的状态
- import java.util.*;
-
- public class Main{
- static int N = 9, cnt;
- static char[] str;//输入输出
- static int[] row = new int[N];//行
- static int[] cel = new int[N];//列
- static int[][] cell = new int[3][3];//九宫格
- static int[] ones = new int[1 << N];//二进制状态中1的个数
- //二进制数中最后一个1的位置
- static Map<Integer, Integer> map = new HashMap<>();
-
- //初始化所有行列和九宫格都可以填1到9中的数
- public static void init(){
- for(int i = 0; i < N; i ++) row[i] = cel[i] = (1 << N) - 1;
-
- for(int i = 0; i < 3; i ++){
- for(int j = 0; j < 3; j ++){
- cell[i][j] = (1 << N) - 1;
- }
- }
- }
-
- //st为true表示在这个位置上填上一个t,在这个位置上去除t
- public static void draw(int x, int y, int t, boolean st){
- if(st) str[x * N + y] = (char)(t + '1');
- else str[x * N + y] = '.';
-
- int v = 1 << t;//第几位的1
- if(!st) v = -v;//回溯
-
- row[x] -= v;
- cel[y] -= v;
- cell[x / 3][y / 3] -= v;
- }
-
- //lowbit函数求二进制数的最后一个1
- public static int lowbit(int x){
- return x & -x;
- }
-
- //获取他能够放的所有的状态为1的位置
- public static int get(int x, int y){
- return row[x] & cel[y] & cell[x / 3][y / 3];
- }
-
- public static boolean dfs(int cnt){
- if(cnt == 0) return true;//如果空点已经没有了
-
- int minv = 10;//能填的数的个数
- int x = -1, y = -1;//初始化这个点的横纵坐标
- //找到能填的数最少的点
- for(int i = 0; i < N; i ++){
- for(int j = 0; j < N; j ++){
- if(str[i * N + j] == '.'){//空着的点才能填
- int f = get(i, j);//获取它能够放的状态
- if(ones[f] < minv){
- minv = ones[f];
- x = i;
- y = j;
- }
- }
- }
- }
- int state = get(x, y);//找到分支最小的那一点
- for(int i = state; i != 0; i -= lowbit(i)){
- int t = map.get(lowbit(i));
- draw(x, y, t, true);//进行放置
- if(dfs(cnt - 1)) return true;
- draw(x, y, t, false);//回溯
- }
-
- return false;//不行就返回false
- }
-
- //main函数
- public static void main(String[] args){
- Scanner sc = new Scanner(System.in);
-
- //获取每个状态中1的个数
- for(int i = 0; i < 1 << N; i ++){
- for(int j = 0; j < N; j ++){
- ones[i] += i >> j & 1;
- }
- }
-
- //二进制数中最后一个1的位置
- for(int i = 0; i < N; i ++) map.put(1 << i, i);
-
- while(true){
- String s = sc.next();//输入数据
- if(s.equals("end")) break;
- str = s.toCharArray();//转化为数组
-
- init();//初始化
-
- cnt = 81;//一共有81个空位要填
- for(int i = 0; i < N; i ++){
- for(int j = 0; j < N; j ++){
- //转化为二维数组来看
- if(str[i * N + j] != '.'){
- draw(i, j, str[i * N + j] - '1', true);//填上这个数
- cnt --;//减去空着的点数
- }
- }
- }
-
- dfs(cnt);
-
- //把char数组转化为字符串输出
- System.out.println(new String(str));
- }
- }
- }
-
- import java.util.*;
-
- public class Main{
- static int N = 70;
- static int length, sum, n;
- static int[] w = new int[N];//每根小棍的长度
- static boolean[] st = new boolean[N];//标记这个小棍用过了没有
-
- public static boolean dfs(int u, int s, int start){//第几根大棍,这个大棍的长度,从第几根小棍开始枚举
- if(u * length == sum) return true;
- if(s == length) return dfs(u + 1, 0, 0);//如果当前大棍已经满了
-
- for(int i = start; i < n; i ++){
- if(st[i]) continue;//已经用过了
- //可行性剪枝
- if(s + w[i] > length) continue;//超过大小了
-
- st[i] = true;//标记一下
- if(dfs(u, s + w[i], i + 1)) return true;
- st[i] = false;//恢复现场
-
- if(s == 0) return false;//放在第一个位置失败
- if(s + w[i] == length) return false;//放在最后一个位置失败
-
- int j = i;
- while(j < n && w[i] == w[j]) j ++;
- i = j - 1;
- }
- return false;
- }
-
- public static void main(String[] args){
- Scanner sc = new Scanner(System.in);
- while(true){
- length = 0;
- sum = 0;
- Arrays.fill(st, false);//多组测试数据
-
- n = sc.nextInt();
- if(n == 0) break;
- for(int i = 0; i < n; i ++){
- w[i] = sc.nextInt();
- length = Math.max(length, w[i]);
- sum += w[i];//所有大棍的总长度
- }
-
- //从大到小排序,优化搜索顺序
- Arrays.sort(w, 0, n);
- int[] b = new int[n];
- for(int i = n - 1, j = 0; i >= 0; i --){
- b[j ++] = w[i];
- }
- for(int i = 0; i < n; i ++){
- w[i] = b[i];
- }
-
- while(true){
- //判断总长度是不是length的倍数
- if(sum % length == 0 && dfs(0, 0, 0)){
- System.out.println(length);//因为length从小到大枚举,所以第一次成立的时候就是最小值
- break;
- }
- length ++;
- }
- }
- }
- }
还不是太懂,先跳一下