历届试题 分考场

问题描述

  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
样例输入
5
10
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
样例输出
5
思路:我的第一个思路是用贪心法是指从第一个同学开始分配考场,然后遍历后面的学生如果和第一个同学不认识,并且不和这个房间其余的学生认识,那么他就可以加进来,第一个考场加入学生完毕。在从第二个没有分配考场的学生分配考场,按照同样的方法该考场加入学生,依次类推。最终结果发现只通过两组测试用例,分析结果不对的原因是贪心法不保证结果是最优的,所以考场数不是最少的。先把代码贴出来,希望以后看到能反思。
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int Amap[1001][1001];
 4 int p[1001];
 5 vector<int> a[1001];
 6 int n,m;
 7 int main()
 8 {
 9     cin >> n >> m;
10     int a1,a2;
11     memset(Amap,0,sizeof(Amap));
12     memset(p,0,sizeof(p));
13     for(int i=1;i<=m;i++){
14         cin >> a1 >> a2;
15         Amap[a1][a2]=1;
16         //Amap[a2][a1]=1;
17     }
18     int num=0;
19     int flag;
20     for(int i=1;i<=n;i++){
21             if(p[i]==1) continue;
22 
23            //  cout << i <<" ";
24         for(int j=i+1;j<=n;j++){
25                 if(i==j) continue;
26                 flag=0;
27             if(Amap[i][j]==0&&Amap[j][i]==0&&p[j]==0){
28 
29             for(vector<int>::iterator iter=a[i].begin();iter!=a[i].end();++iter){
30                 if(Amap[*iter][j]==1 || Amap[j][*iter]==1) flag=1;
31             }
32             if(flag==0){
33                 a[i].push_back(j);
34            // cout << j << " ";
35                 p[j]=1;
36             }
37             }
38         }
39              //cout << endl;
40         p[i]=1;
41         num++;
42 
43     }
44     cout << num << endl;
45     return 0;
46 }

百度之后可以考虑使用dfs,

每新加进来一个人,都与已经开设的教室里面的人进行对比,如果找到一个教室满足,教室里面所有的人都不和新加入的人认识,那么就考虑将这个人加进来。再回溯。最后,当所有教室都不满足的时候,新增教室。参考代码如下

  1 #include<iostream>
  2 
  3 #include<vector>
  4 
  5 using namespace std;
  6 
  7 #define MAXN 110
  8 
  9 #define INF 0x3f3f3f3f;
 10 
 11 int graph[MAXN][MAXN];//建立图
 12 
 13 int cun[MAXN][MAXN];//存储第几个教师有谁谁谁
 14 
 15 int cnt[MAXN]={0};//记录每个教室的人数
 16 
 17 int res=INF;//假设需要一个无穷多的教室
 18 
 19 int n,m;//定义人数,以及认识的数目
 20 
 21 void solve(int id,int num)//回溯搜索,id表示当前安排的是第id的同学,num表示当前安排这个id同学的时时候已经用了多少个教室
 22 
 23 {
 24 
 25         if(num>=res)//如果当前的教室方案已经超过以前的方案了,那么就放弃返回
 26 
 27         return;
 28 
 29     if(id>n)//如果说id的同学的id>n说明全部的同学都安排完了
 30 
 31         {
 32 
 33         res=num;//如果执行到这一步说明res一定是.>num的,所以更新一下
 34 
 35         return;//返回
 36 
 37         }
 38 
 39     for(int i=0;i<num;i++)//扫描每一个已经存在的教室
 40 
 41     {
 42 
 43         int len=cnt[i];//取一下当前i教室的人数
 44 
 45         int c=0;//这个变量表示当前id的同学和这个教室的同学不认识的人数
 46 
 47         for(int j=0;j<len;j++)//扫描这个教室的每一个同学
 48 
 49         {
 50 
 51             if(graph[id][cun[i][j]]==0)//如果没有关系
 52 
 53                 c++;//加1
 54 
 55         }
 56 
 57         if(c==len)//如果全部不认识
 58 
 59         {
 60 
 61             cun[i][cnt[i]++]=id;//那么id同学就进入这个教室,同时这个教室的人数+1
 62 
 63             solve(id+1,num);//那么安排下一个教室
 64 
 65             cnt[i]--;//递归返回之后把这个同学在从这个教室分走,分到下几个教室看看
 66 
 67         }
 68 
 69     }
 70 
 71     cun[num][cnt[num]++]=id;//如果全部教室都存在id认识的同学,那么就把这个同学新开个教室
 72 
 73     solve(id+1,num+1);//递归下一个同学并且当前的教室数目+!
 74 
 75     cnt[num]--;//返回后移除这个同学看下上一个同学有没有其他的选择
 76 
 77 }
 78 
 79 int main()
 80 
 81 {
 82 
 83     cin>>n>>m;
 84 
 85     for(int i=0;i<m;i++)
 86 
 87     {
 88 
 89         int a,b;
 90 
 91         cin>>a>>b;
 92 
 93         graph[a][b]=1;
 94 
 95         graph[b][a]=1;//如果两个人认识的话就可以标记成1,c++在数组创建 的时候已经快速初始化
 96 
 97     }
 98 
 99     solve(1,0);//进入回溯更新最小的教室数目
100 
101     cout<<res;//输入所有可能中的最小的答案
102 
103 }

 

posted @ 2019-03-17 20:29  你的雷哥  阅读(991)  评论(0编辑  收藏  举报