[每日一题] 3.28 历届试题 分考场[图的着色]

历届试题 分考场

问题描述
  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

这应该是一道图的着色的题目
我最开始觉得应该是这么做的:

设d(S)表示把结点集S染色,所需要颜色数的最小值,则d(S)=d(S-S’)+1,其中S’是S的子集,并且内部没有边(即不存在S’内的两个结点u和v使得u和v相邻)。换句话说,S’是一个“可以染成同一种颜色”的结点集
——来自《算法竞赛入门经典(第二版)》P286

但是发现2^100这是什么神仙数字???所以只能用回溯法做了

#include<cstdio> 
#include<algorithm>

using namespace std;

const int MAXN = 105;
int frien[MAXN][MAXN], room[MAXN], n, m, ans = 100; // frien[i][0]存储第i个人有多少个朋友 
bool hasFrien[MAXN];

void read () {
	scanf("%d\n%d", &n, &m);
	for (int i = 0, a, b; i < m; i++) {
			scanf("%d %d", &a, &b);
			if (a > b) {
				frien[a][0]++;
				frien[a][frien[a][0]] = b;
			} else {
				frien[b][0]++;
				frien[b][frien[b][0]] = a;
			}
		}
	}

void dfs (int cur, int cnt) {
	if (cur == n+1) {
		ans = min(ans, cnt);
	} else {
		if (cnt >= ans) return; // 剪枝,现在的教室已经大于等于最多的教室了,在怎么安排都不能是答案 
		// 查看那些教室可以被放人
		for (int i = 1; i <= cnt; i++) {
			hasFrien[i] = false;
		}
		for (int i = 1; i <= frien[cur][0]; i++) {
			hasFrien[room[frien[cur][i]]] = true;
		}
		// 安排一间旧教室
		for (int i = 1; i <= cnt; i++) {
			if (!hasFrien[i]) {
				// 安排一间旧教室 
				room[cur] = i;
				dfs(cur+1, cnt);	
				room[cur] = 0;
			}
		}
		// 安排一间新教室 
		room[cur] = cnt+1;
		dfs(cur+1, cnt+1);	
		room[cur] = 0;
	}
}

int main () {
	read();
	dfs(1, 1);
	printf("%d", ans);
	return 0;
}

其实我有一个疑惑,那么在图的染色的问题时,位压dp什么时候有用呢?

猜你喜欢

转载自blog.csdn.net/AdamAndTina/article/details/88861786