首页 > 试题广场 >

小心火烛的歪

[编程题]小心火烛的歪
  • 热度指数:5520 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 512M,其他语言1024M
  • 算法知识视频讲解
\hspace{15pt}小歪正在一个占地 n \times m 大小的草地上研究他的燃放烟花计划。其中,一些位置已经堆放了杂物,为了便于观察,我们将给出一个 n \times m 大小的字符矩阵描述草地。其中,堆放了杂物的位置使用数字 1 标注;其余位置使用数字 0 标注。
\hspace{15pt}小歪已经做好了若干个烟花燃放计划,每一个计划均为一个 n \times m 大小的字符矩阵,一一对应草地的每一个方格。在这个计划中,将会被燃放烟花的地块使用数字 1 标注;没有烟花的地块使用数字 0 标注。
\hspace{15pt}他想选择一些计划同时实施,如果某个地块在任意一个计划中被标注为燃放,那么这个地块就会真的燃放上烟花。小歪想要知道,是否存在这样一种选择方法,使得全部有杂物位置均不会燃放烟花,而没有杂物的位置全部燃放上烟花;如果存在,请输出最少数量的计划。

输入描述:
\hspace{15pt}第一行输入三个整数 n,m,q \left( 1 \leqq n,m,q \leqq 7 \right) 代表草地的长、草地的宽、计划数量。
\hspace{15pt}此后 n 行,每行输入 m 个字符,代表草地初始状态。
\hspace{15pt}此后 n \times q 行,每行输入 m 个字符,用于描述计划。
\hspace{15pt}全部字符仅为数字 \texttt{0}\texttt{1}


输出描述:
\hspace{15pt}如果不存在满足要求的燃放方案,直接输出 -1

\hspace{15pt}否则,请按如下格式输出:
\hspace{15pt}第一行上输出一个整数 p \left( 0 \leqq p \leqq q \right) 代表使用到的计划数量。
\hspace{15pt}第二行输出 p 个整数代表你所选择的计划编号。编号即输入顺序,从 1 开始计数。

\hspace{15pt}如果存在多个解决方案,您可以输出任意一个,系统会自动判定是否正确。注意,自测运行功能可能因此返回错误结果,请自行检查答案正确性。
示例1

输入

2 2 1
00
01
11
10

输出

1
1
示例2

输入

7 7 5
1110111
1111111
1100001
0101000
1100001
1111111
1110111
0001000
0000000
0000000
1000001
0000000
0000000
0001000
0000000
0000000
0011100
0000000
0011100
0000000
0000000
0000000
0000000
0000010
0000111
0000010
0000000
0000000
0000000
0000000
0010000
0010000
0010000
0000000
0000000
0000000
0000000
0010000
0010111
0010000
0000000
0000000

输出

4
1 2 3 4

说明

\hspace{15pt}草地初始状态如下图所示。在这个样例中,选择 1,2,3,5 也是一个合法的答案。

普通方法 用回溯,进阶方法 用位运算+状态压缩
import java.util.Scanner;
import java.io.*;

// 数据很小, 回溯枚举检查:可以使用 位运算 + 状态压缩
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] sp = br.readLine().split(" ");
        int n = Integer.parseInt(sp[0]);
        int m = Integer.parseInt(sp[1]);
        int q = Integer.parseInt(sp[2]);
        int[] grass = new int[n]; // 草地n行, 合并每行的宽仅1个数表示
        for (int i = 0; i < n; i++) {
            grass[i] = Integer.parseInt(br.readLine(), 2); // 01字符串转int
        }
        int[][] plans = new int[q][n]; // q个计划
        for (int k = 0; k < q; k++) {
            for (int i = 0; i < n; i++) {
                plans[k][i] = Integer.parseInt(br.readLine(), 2);
            }
        }

        int min = Integer.MAX_VALUE, res = 0;
        next:
        for (int i = 0; i < (1 << q); i++) { // q个计划, [0, 2^q-1]种状态 00..00 ~ 11..11
            int cnt = 0;
            int[]  p = new int[n];
            for (int x = i, k = 0; x > 0; x >>= 1, k++) { 
                if ((x & 1) == 1) { // 对于特定状态i, 二进制1 取计划
                    cnt++;
                    if (!check1(grass, plans[k])) continue next;
                    for (int j = 0; j < n; j++) {
                        p[j] |= plans[k][j];
                    }
                }
            }
            if (check2(grass, p, m)) { // 非杂物部分的0 都填满1
                if (cnt < min) {
                    min = cnt;
                    res = i;
                }
            }
        }

        StringBuilder sb = new StringBuilder(); // 处理结果
        if (min == Integer.MAX_VALUE) {
            sb.append(-1);
        } else {
            sb.append(min).append('\n');
            for (int i = 0; res > 0; i++, res >>= 1) {
                if ((res & 1) == 1) {
                    sb.append(i + 1).append(' ');
                }
            }
        }
        System.out.print(sb.toString());
    }

    // 检查plan没有点燃grass杂物:plan[i] & gras[i]==0
    private static boolean check1(int[] grass, int[] plan) {
        for (int i = 0; i < grass.length; i++) {
            if ((grass[i] & plan[i]) > 0) return false;
        }
        return true;
    }

    // 检查plan填满grass非杂物部分:plan[i] | gras[i]== (1<<m)-1
    private static boolean check2(int[] grass, int[] plan, int m) {
        for (int i = 0; i < grass.length; i++) {
            if ((grass[i] | plan[i]) != (1 << m) - 1) return false;
        }
        return true;
    }
}


发表于 2025-10-09 15:19:43 回复(0)
//成功啦 自己做不出来的原因 在于 烟花计划有共同的1!!!即燃放位置  这样是可以的 符合题意 例如例题里那样的  如何解决这个问题 就欧克la

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    private static class plan {
        long mask;
        int id;
        plan(long mask, int id) {
            this.mask = mask;
            this.id = id;
        }
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        int n = in.nextInt();
        int m = in.nextInt();
        int q = in.nextInt();
        in.nextLine();
        String[] a = new String[n];//记录草坪原始状态
        for (int i = 0; i < n; i++) {
            a[i] = in.nextLine();
        }
        //对草坪进行目标掩码处理 以及 把杂物位置拿出来
        long targetmask = 0;
        List<int[]> b = new ArrayList<>();
        int pos = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (a[i].charAt(j) == '1') {
                    b.add(new int[] {i, j});
                } else {
                    pos = i * m + j;
                    targetmask = targetmask | 1L << pos;
                }
            }
        }
        //好的方法如下 简洁又易懂
        List<plan> c = new
                ArrayList<>(); //存放满足杂物位置的计划 待排序组合
        for (int i = 0; i < q; i++) {
            List<String> d = new ArrayList<>();
            for (int j = 0; j < n; j++) {
                d.add(in.nextLine());
            }
            boolean NO = false;
            for (int[] k : b) { //遍历放杂物的 获取所有的xy值
                int x = k[0];
                int y = k[1];
                if (d.get(x).charAt(y) == '1') {
                    NO = true;
                    break;
                }
            }
            if (NO) {
                continue;
            }
            //通过的烟花计划开始处理 计算目标掩码 并和id一起存储到类Class中
            long mask = 0;
            for (int j = 0; j < n; j++) {
                String x = d.get(j);
                for (int k = 0; k < m; k++) {
                    if (x.charAt(k) == '1') {
                        pos = j * m + k; //0,0 1,0 0,1  0 1 2  01 10 100
                        mask = mask | 1L << pos;
                    }

                }
            }
            c.add(new plan(mask, i + 1));
        }
        /*
        //这部分是可以的 是自己使用自己的方法找到复合条件烟花计划的 并存储 但是时间上更复杂了 因为等于遍历了所有烟花位置与花坪位置比较 看是否为0  但是上面那个方法只找了杂物位置是否放烟花
        //要把List<int[]> b = new ArrayList<>();修改为 int[][] b = new int[n][m];
        //然后开始筛选烟花计划 排除在杂物中 放烟花计划
        List<plan> c =new ArrayList<>();//存放满足杂物位置的计划 待排序组合
        for(int i = 0;i<q;i++){//有q组
            boolean NO = false;
            long mask = 0;
            pos = 0;
            for(int j = 0;j<n;j++){
                String x = in.nextLine();
                if(NO){
                    continue;
                }
                for(int k = 0;k<m;k++){
                    if(b[j][k]=='1'&&x.charAt(k)=='1'){
                        NO = true;
                        break;
                    }else if(x.charAt(k)=='1'){
                        pos = j*m+k;
                        mask = mask | 1L<<pos;
                    }
                }
            }
            c.add(new plan(mask,i+1));//这里特别注意哦 !!添加成功的烟花计划
        }
        */
        if (targetmask == 0) {
            System.out.println(0);
            return;
        }
        if (c.isEmpty()) { //如果都没有符合的 输出-1并终止程序运行啦
            System.out.println(-1);
            return;
        }        
        //现在我们已经筛选完合适的烟花计划了 该开始组合了 总组合次数= 2^烟花计划次方 -1
        int mincount = Integer.MAX_VALUE;
        List<Integer> minyanhua = null;
        for (int i = 1; i < (1<<c.size()); i++) {
            long mask = 0;
            List<Integer> yanhua = new ArrayList<>();
            for (int j = 0; j < c.size(); j++) {
                if ((i & (1 << j)) != 0) {
                    mask = mask | c.get(j).mask;
                    yanhua.add(c.get(j).id);
                }
            }
            if (mask == targetmask) {
                if (yanhua.size() < mincount) {
                    mincount = yanhua.size();
                    minyanhua = yanhua;
                }
            }
        }//这里遍历组合了所有烟花排序 来看结果 如果都没有 就说明都没有符合的 如果有就输出
        if (minyanhua==null) {
            System.out.println(-1);
        } else {
            System.out.println(mincount);
            for (int i = 0; i < minyanhua.size(); i++) {
                System.out.print(minyanhua.get(i)+" ");
            }
        }
        /*
                System.out.println(targetmask);
                for(int i = 0;i<c.size();i++){
                    System.out.println(c.get(i).mask);
                    System.out.println(c.get(i).id);
                }
                for(int i = 0;i<n;i++){
                    System.out.println(a[i]);
                }

                for(int i = 0;i<n;i++){
                    for(int j = 0;j<m;j++){
                        System.out.print(b[i][j]+" ");
                    }
                    System.out.println();
                }

                */
    }
}
发表于 2025-03-23 23:11:50 回复(3)

卡在第20个测试用例了

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    private static int min = Integer.MAX_VALUE;
    private static List res = new ArrayList();
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        int q = in.nextInt();
        int[][] grid = new int[n][m];
        int[][] grid1 = new int[n][m];
        List plain = new ArrayList();
        long empty = 0;
        for (int i = 0; i < n; i++) {
            String row = in.next();
            for (int j = 0; j < row.length(); j++) {
                grid[i][j] = row.charAt(j) - '0';
                grid1[i][j] = row.charAt(j) - '0';
                if (grid[i][j] == 0) {
                    empty |= 1L << (i * m + j);
                }
            }
        }
        for (int k = 0; k < q; k++) {
            int[][] g = new int[n][m];
            for (int i = 0; i < n; i++) {
                String row = in.next();
                for (int j = 0; j < row.length(); j++) {
                    g[i][j] = row.charAt(j) - '0';
                }
            }
            plain.add(g);
        }
        dfs(0, plain, grid, grid1, empty, 0, new ArrayList());
        int size = res.size();
        if (size == 0) {
            System.out.println(-1);
        } else {
            System.out.println(size);
            for (int i = 0; i < size; i++) {
                System.out.print(res.get(i));
                if (i != size - 1) {
                    System.out.print(" ");
                }
            }
        }
    }
    public static Object[] add(int[][] grid, int[][] grid1, int[][] plain, long empty) {
        int m = grid.length;
        int[][] newGrid = new int[m][grid[0].length];
        for (int i = 0; i < m; i++) {
            newGrid[i] = Arrays.copyOf(grid[i], grid[i].length);
            for (int j = 0; j < grid[i].length; j++) {
                if (grid1[i][j] == 1 && plain[i][j] == 1) {
                    return new Object[]{false, 0L, null};
                }
                if (newGrid[i][j] == 0 && plain[i][j] == 1) {
                    empty |= 1L << (i * m + j);
                    newGrid[i][j]++;
                }
            }
        }
        return new Object[]{true, empty, newGrid};
    }
    private static void dfs(int i, List plain, int[][] grid, int[][] grid1, long empty, long currEmpty,
                            List path) {
        if (i == plain.size()) {
            if (currEmpty == empty) {
                if (path.size() < min) {
                    min = path.size();
                    res = new ArrayList(path);
                }
            }
            return;
        }
        Object[] ob = add(grid, grid1, plain.get(i), currEmpty);
        boolean valid = (boolean) ob[0];
        dfs(i + 1, plain, grid, grid1, empty, currEmpty, path);
        if (valid) {
            long newEmpty = (long) ob[1];
            int[][] newGrid = (int[][]) ob[2];
            path.add(i + 1);
            dfs(i + 1, plain, newGrid, grid1, empty, newEmpty, path);
            path.remove(path.size() - 1);
        }
    }
}

这全是障碍物,也没法种啊

2 2 5
11
11
10
00
01
00
11
11
00
10
00
01
发表于 2025-03-17 13:35:51 回复(1)
麻了,这是简单题?只过两个案例,输入太复杂想调都难
//HJ95 小心火烛的歪
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        //草地的长、草地的宽、计划数量
        String[] nums = in.nextLine().split(" ");

        int n = Integer.parseInt(nums[0]);
        int m = Integer.parseInt(nums[1]);
        int q = Integer.parseInt(nums[2]);

        //草坪:单个n*m
        char[][] grass = new char[n][m];

        //初始化草坪
        for (int i = 0; i < n; i++) {
            char[] line = in.nextLine().toCharArray();
            for (int j = 0; j < m; j++) {
                grass[i][j] = line[j];
            }
        }

        //满足的计划编号
        List<String> res = new ArrayList<>();
        //从1开始计数
        for (int k = 1; k <= q; k++) {
            //该计划是否满足
            boolean flag = true;
            for (int i = 0; i < n; i++) {
                char[] line = in.nextLine().toCharArray();
                for (int j = 0; j < m; j++) {
                    //有杂物就不能放,没有杂物可放可不放
                    if (grass[i][j] == '1' && line[j] == '1') {
                        flag = false;
                        break;
                    }
                }
            }
            //否则就是满足的
            if (flag) {
                res.add(k + "");
            }
        }

        //输出
        System.out.println(res.size());
        System.out.println(String.join(" ", res));
    }



发表于 2025-03-14 18:26:26 回复(1)