POJ1419 Graph Coloring

题意:给定一个n(1 <= n <= 100)个点,m条边的无向图,每条边只能选1个点,输出最多能选的点数,并打印方案。

分析:

本周7道作业最难的题,是一个一般图的最大独立集(最大独立集含义如题),这种问题一般把原图所有未相连的点连接,再把原图所有相连点的边去掉,也就是建一个反图,然后求最大团,所谓最大团,就是求一个最大的子图,使得其构成一个完全图,额...所谓完全图,就是任意两点之间都有边相连的图。

最大团的求法就是用dfs,每次枚举要加进去的点,如果这个点和已经加进去的点都有边相连,那么就加进去,如果不能加进去任何点了,现在存的就是一个团。

加了若干剪枝(虽然说没这些剪枝估计也能过):

1.每次只枚举当前加进去的结点相邻的点,因为不相邻的点肯定不能加(可行性剪枝)。

2.枚举的点序号大于当前加进去的点才考虑(避免重复搜索)。

3.如果最好情况下也不能优于当前解,直接退出(最优性剪枝)。

然后成功达到G++排行榜82名,GG。

打印方案的时候就是如果当前是最优解的话,就打印,并把fin(完成标志)设为1.

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;

int T, n, m, x, y, cnt, ans, ansi, fin, vis[105], mat[105][105], to[10005], nxt[10005], hd[105];
vector<int> v;

void add(int x, int y) {
	to[++cnt] = y;
	nxt[cnt] = hd[x];
	hd[x] = cnt;
}

int dfs(int x, int cnt, int fl) {
	if(n - x + cnt <= ans) return 0;
	v.push_back(x);
	vis[x] = 1;
	int ok = 0, anss = 0;
	for(int i = hd[x]; i; i = nxt[i]) if(!vis[to[i]] && to[i] > x){
		int okk = 1;
		for(int j = 0; j < v.size(); j++) if(!mat[to[i]][v[j]]) {
			okk = 0;
			break;
		}
		if(okk) ok = 1, anss = max(anss, dfs(to[i], cnt+1, fl));
	}
	if(!ok && cnt == ans && !fin && fl) {
		fin = 1;
		for(int i = 0; i < v.size(); i++) printf("%d ", v[i]);
	}
	vis[x] = 0;
	v.pop_back();
	if(!ok) return cnt;
	return anss;
}

int main() {
	scanf("%d", &T);
	while(T--) {
		cnt = 0;
		memset(hd, 0, sizeof hd);
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
			mat[i][j] = 1;
		while(m--) scanf("%d%d", &x, &y), mat[x][y] = 0, mat[y][x] = 0;
		for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
		if(mat[i][j]) add(i, j);
		ans = 0;
		for(int i = 1; i <= n; i++) {
			int tmp = dfs(i, 1, 0);
			if(ans < tmp) ans = tmp, ansi = i;
		}
		printf("%d\n", ans);
		fin = 0;
		dfs(ansi, 1, 1);
		printf("\n");
	}
	return 0;
} 


你可能感兴趣的:(DFS,剪枝,最大独立集,最大团)