题目
思路
前置知识,hdu4751二分图。
首先本题跟hdu4751有点像,a和b不认识,b和c不认识,那么必然就有a和c在一个组。如果这时a和c再不认识,就是无解的情况。
所以上来首先给本题来个二分图判定,并分出连通块,此时的所有连通块都应当是二分图。
根据某一个连通块上的交叉染色,分出这个连通块的team0和team1。
那么现在就有两种选择,第一种是team0成为我们需要的team0,team1成team1。;一种是team1成team0,team0成team1。
最终我们是要让两队人数相同。我们算出每个连通块team0和team1的差值,这些差值加起来应该越靠近0越好。
这里就抽象出背包模型来了,只不过这次的总和不是某个值而是0。
本题的代码实现较复杂,有很多细节值得学习,而print函数也是懒得写搬过来LRJ,今后应该多看看。。
代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;
const int maxn = 100 + 10;
int n, cc, know[maxn][maxn], G[maxn][maxn], color[maxn], d[maxn][3*maxn], diff[maxn];
vector<int> team[maxn][2]; // team[i][j]表示第i个连通图里的分组为j(0或1)的组员们
bool bfs(int u, int c, int cnt) {
color[u] = c;
team[cnt][c - 1].push_back(u);
_rep(v,1,n)
if (v != u && G[u][v]) {
if (color[v] == c) return false;
if (color[v] == 0 && !bfs(v, 3 - c, cnt)) return false;
}
return true;
}
bool erfen() {
cc = 0;
memset(color, 0, sizeof(color));
memset(diff, 0, sizeof(diff));
_rep(i, 1, n)
if (color[i] == 0) {
team[cc][0].clear();
team[cc][1].clear();
if (!bfs(i, 1, cc))
return false;
diff[cc] = team[cc][0].size() - team[cc][1].size();
cc++;
}
return true;
}
void print(int ans) {
vector<int> team1, team2;
for (int i = cc - 1; i >= 0; i--) {
int t;
if (d[i][ans - diff[i] + n]) { t = 0; ans -= diff[i]; }
else { t = 1; ans += diff[i]; }
for (int j = 0; j < team[i][t].size(); j++)
team1.push_back(team[i][t][j]);
for (int j = 0; j < team[i][1 ^ t].size(); j++)
team2.push_back(team[i][1 ^ t][j]);
}
printf("%d", team1.size());
for (int i = 0; i < team1.size(); i++) printf(" %d", team1[i]);
printf("\n");
printf("%d", team2.size());
for (int i = 0; i < team2.size(); i++) printf(" %d", team2[i]);
printf("\n");
}
void dp() {
memset(d, 0, sizeof(d));
d[0][0 + n] = 1; // 负下标访问问题
_for(i, 0, cc) {
_rep(j, -n, n) if(d[i][j+n]) {
d[i+1][j + diff[i] + n] = 1;
d[i+1][j - diff[i] + n] = 1;
}
}
for (int ans = 0; ans <= n; ans++) {
if (d[cc][ans + n]) { print(ans); return; }
if (d[cc][-ans + n]) { print(-ans); return; }
}
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
memset(know, 0, sizeof(know));
memset(G, 0, sizeof(G));
scanf("%d", &n);
int v;
_rep(u, 1, n) {
while (1) {
scanf("%d", &v);
if (v == 0) break;
know[u][v] = 1;
}
}
// 根据不认识关系建图
_rep(u, 1, n)
_rep(v, 1, n)
if(u!=v)
if (!know[u][v] || !know[v][u])
G[u][v] = G[v][u] = 1;
if (!erfen()) printf("No solution\n");
else {
dp();
}
if (T) printf("\n");
}
return 0;
}