欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

蓝桥杯分考场

程序员文章站 2022-05-20 22:33:40
...

因为数据量很小可以使用回溯算法。
应用两层回溯:
第一层回溯是将考生放在不同考场里面产生的效果,比如学生3号可以放在教室1和教室2中那么放在那一个教室会产生更好的效果这是一层回溯。
第二层回溯是考生放入以前的考场还是考生自己重新用一个考场。比如考生3号可以放进教室1和教室2,也可以放进教室3。
应用简单的剪枝技巧:
现在考场的数量已经大于以前最少的考场数量了就不用在展开了。
因为题目中没有时间限制,所以可以不使用vector,使用二维数组存放边,使用二维数组存放教室中学生。
另外使用vecot中的find()总是报错所以就不使用了
使用二维数组的时间复杂度约为O(nn)因为没一个学生需要遍历之前的考场就需要遍历每个人一次1 + 2…+n - 1;
使用vector最好的情况是NlogN,这种情况对应只有一个考场,每个学生耗费logN的复杂度,NlogN,最坏的情况就会退成O(N
N),对应着N个考场,每个之前的学生都需要遍历一次。

#include<stdio.h>
#include<iostream>
#include<vector>
#include<string.h>
using namespace std;
const int maxn = 100 + 5;
int room[maxn][maxn];//保存教室i中第j个学生的id
int gra[maxn][maxn];//存图
int m,n;
int res = maxn + 500;//保存答案
void solve(int id,int num)
{
    //printf("%d %d\n",id,num);
    if(num >= res){//剪枝
        return ;
    }
    if(id > n){//学生的编号从1开始防止room[][] = 0不是没学生的标志
        res = min(res,num);
        return ;
    }
    for(int i = 1;i <= num;i++){//找到id是否有符合的教室
        int k = 0,flag = 1;
        while(room[i][k]){//查找学生教室中是否有认识的人
           // printf("%d %d %d\n",id,room[i][k],gra[id][room[i][k]]);
            if(gra[id][room[i][k]]){//教室里面有认识的人
                //printf("冲突\n");
                flag = 0;
                break;
            }
            k++;//不要写在数组里面防止执行顺序的不对而出错
        }
        if(flag){				//回溯一定对应着判断
            room[i][k] = id;
            solve(id+1,num);
            room[i][k] = 0;		//第一层回溯
        }
    }
//重新开启一个教室
    room[num+1][0] = id;
    solve(id+1,num+1);
    room[num+1][0] = 0;//第二层回溯
}
void init()
{
    memset(room,0,sizeof(room));
}
int main()
{
    init();
    scanf("%d%d",&n,&m);
    for(int i = 0;i < m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        gra[a][b] = gra[b][a] = 1;
    }
    solve(1,0);
    printf("%d\n",res);
    return 0;
}

相关标签: 回溯