回溯法--蓝桥杯往年真题之分考场

问题描述
  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
样例输出
4
题解
一开始我的解法是比较单纯(蠢),写了个三重循环,提交之后总是只有60分,最后发现了按照我当时的做法有部分数据是错误的,所以写这个博客给自己加深印象。


错误代码

public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); //学生的人数 即为图的点数
int s = sc.nextInt(); //相认识的对数 即为图的边数
int[][] g = new int[105][105]; //学生关系图
int[] c = new int[105]; //颜色数组
Arrays.fill(c, 0); //颜色数组全部初始化为0
for (int i = 1; i <= s; i++) {
int a = sc.nextInt();
int b = sc.nextInt();
g[a][b] = 1;
g[b][a] = 1;
}

    for (int i = 1; i <= n; i++) {
        if (c[i] == 0) {
            c[i] = 1;
        }
        for (int j = 1; j <= n; j++) {
            if (i == j){
                continue;
            }
            else if (g[i][j] == 1 && c[j] == 0) {
                c[j] = 1;
                for (int z = 1; z <= n; z++) {
                    if (j == z) {
                        continue;
                    }
                    else if (g[j][z] == 1 && c[z] != 0 && c[z] == c[j]) {
                        c[j]++;
                    }
                }
            }
        }
    }
    int sum = c[1];
    for (int k = 1; k <= n; k++) {
        System.out.println(c[k] + "and" + k);
        if (c[k] > sum) {
            sum = c[k];
        }
    }   
    //System.out.println(sum);
}

}



后面找出了一组上面的代码通过不了的数据:
11
7
1 9
2 4
2 6
2 9
5 8
5 10
8 8
后面自己分析了一下,我的错误代码只能满足题目要求的认识的人不在同一个班,但不满足最小数目的要求。 因为我的错误代码的做法是,首先给每一个即将要被安排的学生安排到第一个教室,然后有认识的人就向后移到下一个教室。但是有可能存在这种情况,把即将要被安排的学生直接放到后几个教室(假设前面的教室该学生可以进入)到最后安排的教室的数目更少。
后面查了网上别人的做法,基本上用的都是回溯法,就是可以先试其中一种安排,然后回到原来的状态可以再试另外的安排,找出最少数目的。然后我就自己重新写了一个用这种方法的程序。提交成功。

正确代码

import java.util.*;

public class Main {
static int res = 0x3f3f3f; //定义为无穷大
static int n;
static int m;
static int[][] g = new int[110][110];
static int[] c = new int[110];

public static boolean judge (int s, int r) {
    for (int i = 1; i <= n; i++) {
        if (g[s][i] == 1 && c[i] == r) {
            return false;
        }
    }
    return true;
}
public static void F (int s, int r) { //s 代表第几个学生,r代表目前有几个房间
    //System.out.println("a");
    if (r >= res) {
        return;
    }

    if (s > n) {
        res = (res>r)?r:res; //取两者中更小的
        return;
    }

    for (int i = 1; i <= r; i++) {
        if (judge(s, i)) {
            c[s] = i;
            F(s+1, r);
            c[s] = 0;
        }
    }
    c[s] = r+1;
    F(s+1, r+1);
    c[s] = 0;
}
public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    n = sc.nextInt();
    m = sc.nextInt();

    for (int i = 1; i <= m; i++) {
        int a = sc.nextInt();
        int b = sc.nextInt();
        g[a][b] = g[b][a] = 1;
    }
    F(1, 0);
    System.out.println(res);
}

}



这个代码通过先对其中一种情况进行测试,然后返回将已经安排的学生标记为0再进行测试。其中要记住第一次调用F函数的时候,第二个参数要为0,如果是1的话程序会出现超时的情况。其实这就是一个深度搜索的问题,通过画树状图可以更好的理解这个程序。

猜你喜欢

转载自www.cnblogs.com/Reton/p/12315021.html
今日推荐