POJ_1112_Team Them Up!


POJ_1112_Team Them Up!

问题:有N个人,他们两两之间存在认识关系,例如:A,B两个人,存在4种关系,A认识B,B不认识A;B认识A,A不认识B;A不认识B并且B不认识A(互相不认识);A认识B并且B认识A(互相认识);

问题:需要把这N个人分成2组,每组至少1个人,每组中任意2个人都要互相认识,要求2组人员的差值最小;

解决:

1、只有互相认识的人才能放在同一个组,也就是说前面3中情况都不能放在一个组;

2、在满足前面3中情况的两两之间连线,建立无向图G(可能不是连通图,存在连通分支);

3、在G中连线的2个人肯定是不能放在同一个分组里面,于是DFS染色,判断G是否是二分图。如果图G不是二分图,那么肯定无解;

如果G是二分图,计算每个连通分支中分成属于x[i],y[i]2个组的人数(i表示连通分支);

4、对X,Y 2组人员DP(经典的背包问题);可以对2个组之间的差值DP范围是(-100~+100),也可以对2个组的人数DP;我选择的是后一种办法。

dp[i][j](0<=i,j<=100)表示第1组为i个人,第2组为j个人这个状态;dp[i][j]=1表示存在。初始dp[0][0] = 1;然后对每个连通分支DP,转移方程:

if dp[i][j]=1

   dp[i+x[k]][j+y[k]] = 1

   dp[i+y[k]][j+x[k]] = 1

最后取个差值最小的;

 

例如:

5
2 3 5 0
1 4 5 3 0
1 2 5 0
1 2 3 0
4 3 2 1 0

最后建立图
2 - 1
2 - 3
2 - 5

DFS染色求得的分组(顺序不重要):
x y
3 1
1 0

然后DP
对于第1个连通分支:x[0]=3, y[0]=1
dp[0+x[0]][0+y[0]] = dp[0][0] = 1;
dp[0+y[0]][0+x[0]] = dp[0][0] = 1;

即:dp[3][1] = 1, dp[1][3] = 1;

对于第2个连通分支:x[1]=1, y[1]=0;
dp[3+x[1]][1+y[1]] = dp[3][1] = 1;
dp[3+y[1]][1+x[1]] = dp[3][1] = 1;
dp[1+x[1]][3+y[1]] = dp[1][3] = 1;
dp[1+y[1]][3+x[1]] = dp[1][3] = 1;

即:dp[4][1]=1,dp[3][2]=1,dp[1][4]=1,dp[2][3]=1;

#include <iostream> #define inf 1000000 using namespace std; const int MAXN = 101; int map[MAXN][MAXN]; int id[2][MAXN]; int color[MAXN]; int dp[MAXN][MAXN]; int tmp[MAXN][MAXN]; struct { int c1, c2; int i, j; }pre[MAXN][MAXN]; int N, t; bool DFS(int CurV, int c) { color[CurV] = c; id[c%2][t]++; bool ans = true; for (int i = 1; i <= N; i++) { if (map[CurV][i] == 1) { if (color[i] == -1) { ans = DFS(i, (c%2==0)?(c+1):(c-1)); if (ans == false) return false; } else if (color[i] == color[CurV]) return false; } } return true; } int main() { freopen("test.txt", "r", stdin); scanf("%d", &N); memset(map, 0, sizeof(map)); memset(id, 0, sizeof(id)); memset(color, 0xff, sizeof(color)); memset(tmp, 0, sizeof(tmp)); memset(dp, 0, sizeof(dp)); for (int i = 1; i <= N; i++) { int j; while (scanf("%d", &j) != EOF && j != 0) tmp[i][j] = 1; } for (int i = 1; i <= N; i++) { for (int j = i+1; j <= N; j++) { if (!(tmp[i][j] == 1 && tmp[j][i] == 1)) map[i][j] = map[j][i] = 1; } } bool ans; int c = 0; t = 0; for (int i = 1; i <= N; i++) { if (color[i] == -1) { ans = DFS(i, c); c += 2; t++; if (ans == false) break; } } if (ans == false) printf("No solution/n"); else { int n = 0; dp[0][0] = 1; for (int k = 0; k < t; k++) { for (int i = 0; i <= n; i++) { int j = n-i; if (dp[i+id[0][k]][j+id[1][k]] == 0 && dp[i][j] == 1) { pre[i+id[0][k]][j+id[1][k]].c1 = k*2; pre[i+id[0][k]][j+id[1][k]].c2= k*2+1; pre[i+id[0][k]][j+id[1][k]].i = i; pre[i+id[0][k]][j+id[1][k]].j = j; dp[i+id[0][k]][j+id[1][k]] = 1; } if (dp[i+id[1][k]][j+id[0][k]] == 0 && dp[i][j] == 1) { pre[i+id[1][k]][j+id[0][k]].c1 = k*2+1;//记录颜色 pre[i+id[1][k]][j+id[0][k]].c2 = k*2;//记录颜色 pre[i+id[1][k]][j+id[0][k]].i = i; pre[i+id[1][k]][j+id[0][k]].j = j; dp[i+id[1][k]][j+id[0][k]] = 1; } } n += id[0][k]+id[1][k]; } int res = inf; int a, b; for (int i = 1; i <= N; i++) { int j = N-i; if (dp[i][j] == 1 && res > abs(i-j)) { a = i; b = j; res = abs(i-j); } } printf("%d", a); for (int i = a, j = b; !(i == 0 && j == 0);) { int c = pre[i][j].c1; for (int k = 1; k <= N; k++) { if (color[k] == c) printf (" %d", k); } int _i = i; i = pre[i][j].i; j = pre[_i][j].j; } printf("/n"); printf("%d", b); for (int i = a, j = b; !(i == 0 && j == 0);) { int c = pre[i][j].c2; for (int k = 1; k <= N; k++) { if (color[k] == c) printf (" %d", k); } int _i = i; i = pre[i][j].i; j = pre[_i][j].j; } printf("/n"); } return 0; }

 

你可能感兴趣的:(POJ_1112_Team Them Up!)