POJ-1112 Team Them Up! 反图+DP
给定一些关系,求这样的一个划分,满足以下几点:
1、每个组合至少有一个成员;
2、每个人只能属于一个组;
2、每个组内的成员必须相互认识;
3、两个组的成员个数要尽可能的接近。
首先由必须两两认识我们可以将有向图转化为无向图,之后,我们如果直接对这个图求连通分量的话,有以下结论,如果连通分量的个数为2的话,那么直接可以划分了,如果超过两个的话那么肯定就是没有结果,也就是无法进行分组。最难处理的就是所有点的连通的话,那么如何去做一个划分呢,这个就和割点有关系了,分离一个割点会产生多个连通分量,这要必须保证那些多出来的要再同一个集合内,这样这个问题就很复杂了。
简单的做法是建立一个反图,再对这个反图求一次连通分量,那么连通分量之间在原图上就是连通的了,而连通分量的内部点则不满足互相连通,因为这些边是我们添加的边。那么对于一个连通分量的话,我们就直接进行一次dfs染色,再通过相同颜色的边是否在反图中有边来得到矛盾。最后我们只需要把每个连通分量中不同颜色的点划分到任意一个组即可。
求出了每个连通分量之后,我们在用一个f[i][j]表示到第i个连通分量时,是否能够组成j个人一组。f[i][j] = f[i-1][j-size(parti, 0)] || f[i-1][j-size(parti, 1)].
代码如下:
#include <cstdlib> #include <cstring> #include <cstdio> #include <iostream> #include <algorithm> #include <queue> #define MAXN 105 using namespace std; int N, part, kind[MAXN]; bool G[MAXN][MAXN], f[MAXN][MAXN]; vector<int>que[MAXN][2]; bool dfs(int u, int colour) { kind[u] = colour; que[part][colour].push_back(u); for (int i = 1; i <= N; ++i) { if (G[u][i]) { if (kind[i] != -1) { if (kind[i] == kind[u]) { // 如果一个连通分量中的颜色相同且反图中有边则显然不合法 return false; } } else { if (!dfs(i, colour^1)) return false; } } } return true; } void print(int x) { int hash[MAXN], cnt = x; memset(hash, 0, sizeof (hash)); for (int i = N; i >= 1; --i) { if (x >= que[i][0].size() && f[i-1][x - que[i][0].size()]) { x -= que[i][0].size(); for (int k = 0; k != que[i][0].size(); ++k) { hash[que[i][0][k]] = 1; } } else if (x >= que[i][1].size() && f[i-1][x - que[i][1].size()]) { x -= que[i][1].size(); for (int k = 0; k != que[i][1].size(); ++k) { hash[que[i][1][k]] = 1; } } } printf("%d", cnt); for (int i = 1; i <= N; ++i) { if (hash[i]) printf(" %d", i); } printf("\n%d", N-cnt); for (int i = 1; i <= N; ++i) { if (!hash[i]) printf(" %d", i); } puts(""); } void DP() { memset(f, 0, sizeof (f)); f[0][0] = 1; for (int i = 1; i <= part; ++i) { for (int j = 0; j <= N; ++j) { if (f[i-1][j]) { f[i][j+que[i][0].size()] = 1; f[i][j+que[i][1].size()] = 1; } } } for (int i = N/2; i >= 1; --i) { if (f[part][i]) { print(i); break; } } } int main() { int c, flag; while (scanf("%d", &N) == 1) { flag = part = 0; memset(kind, 0xff, sizeof (kind)); memset(G, 0, sizeof (G)); for (int i = 1; i <= N; ++i) { que[i][0].clear(); que[i][1].clear(); } for (int i = 1; i <= N; ++i) { while (scanf("%d", &c), c) { G[i][c] = true; } } if (N <= 1) { puts("No solution"); continue; } for (int i = 1; i < N; ++i) { for (int j = i + 1; j <= N; ++j) { G[i][j] = G[j][i] = !(G[i][j] && G[j][i]); } } for (int i = 1; i <= N; ++i) { if (kind[i] == -1) { // kind[i] 表示这一点的颜色 ++part; if (!dfs(i, 0)) { flag = 1; break; } } } if (flag) puts("No solution"); else { DP(); } } return 0; }