蓝桥杯真题——分考场(DFS+剪枝)

题目:

在这里插入图片描述

输入输出:

在这里插入图片描述

分析:

此题使用深搜来解决,从第一个人开始逐一的分配教室,分配教室的方案如下:遍历已经存在的教室,然后再遍历教室中的所有人,查看是否存在某一个人与当前要被分配教室的人认识,如果此教室中没有认识的人,那么我们可以将当前的人分配到这个教室中,当然也可以选择不分配到当前的教室,因为分配到当前的教室可能不是最优的选择,此时就要用到回溯,回溯的目的是为了寻找最优的解决方案,当遍历当前存在的所有教室之后,还有一种策略是将当前的人分配到一个新的教室,再搜索这一种情况即可,下述代码中有详细的解释。

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#define INF 0x3f3f3f3f
using namespace std;

const int MAXN = 105;
int mat[MAXN][MAXN];  //mat[i][j]代表决定i和j之间是否认识
int cnt[MAXN];  //cnt[i]表示第i个教室的人数
int room[MAXN][MAXN]; //room[i][j]代表第i个教室中第j个人的编号
int n,m,ans;
bool flag;

void Dfs(int person,int num)   //person表示待分配的人 num表示当前教室数量
{
    if(num >= ans) return;    //剪枝 如果当前的教室数量大于ans 说明当前的选择肯定不是最优的
    if(person > n)   //如果person大于n 说明已经分配完了n个人 此时结果与ans相比较 取最优
    {
        ans = min(ans,num);
        return;
    }
    for(int i=0;i<num;++i)
    {
        flag = false;
        int allPep = cnt[i];
        for(int j=1;j<=allPep;++j)
            if(mat[person][room[i][j]])
            {
                flag=true;  //如果person和此教室中的任意一个人是朋友 则不满足条件
                break;
            }
        if(!flag)
        {
            cnt[i]++;  //表示person这个人加入到i教室中
            room[i][cnt[i]] = person;
            Dfs(person+1,num);
            cnt[i]--;   //回溯操作 因为person加入到当前的教室可能不是最优的
        }
    }
    cnt[num]++;  //表示上述可能没有满足教室分配的最优条件 那就重新开一个新教室
    room[num][cnt[num]] = person;
    Dfs(person+1,num+1);
    cnt[num]--;  //回溯 此处表示上一次的教室分配可能不是最优
}

int main()
{
    int a,b;
    ans = INF;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&a,&b);
        mat[a][b] = 1;
        mat[b][a] = 1;
    }
    Dfs(1,0);
    printf("%d\n",ans);
    return 0;
}
发布了61 篇原创文章 · 获赞 7 · 访问量 3635

猜你喜欢

转载自blog.csdn.net/weixin_42469716/article/details/104637914