// uva 1627 Team them up! 二分图 + 连通分量 + 背包
//
// 解题思路:
//
// 同组之内每个人相互认识,如果a和b不相互认识,则
// a和b只能分在两个不同的组.所以以不相互认识作为边
// 建图,对于每个联通块判断是否是二分图.如果不是,没有
// 方案,如果有,那么进行背包.
// d[i][j]表示前i个连通块第一个队伍人数比第二个
// 队伍的人数多j个人是否合法.
// 推出if (d[i][j]) { d[i+1][j+diff[i]] = 1
// d[i+1][j-diff[i]] = 1}
// 因为有负数所以加上一个偏移量就可以了.n足矣.
//
// 初始状态下d[0][0] = 1.现在第二维加上n就ok了
// 然后逆推打印.
//
// 还是很巧妙的,继续加油哟~~~FIGHTING!!!
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <vector>
#define cls(x,a) memset(x,a,sizeof(x))
using namespace std;
const int MAXN = 108;
int n;
int color[MAXN];
int cc;
int g[MAXN][MAXN];
int d[MAXN][MAXN*2];
vector<int> G[MAXN];
vector<int> term[MAXN][2];
bool vis[MAXN];
int diff[MAXN];
void input(){
scanf("%d",&n);
cls(g,0);
cls(color,0);
cls(vis,0);
cls(d,0);
diff[MAXN];
for (int i = 0 ;i <= n;i ++){
term[i][0].clear();
term[i][1].clear();
G[i].clear();
}
for (int i = 1;i <= n;i++){
int v;
while(scanf("%d",&v)!=EOF && v){
g[i][v] = 1;
}
}
if (n == 1){
puts("No solution");
return ;
}
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
if (i != j &&(!g[i][j] || !g[j][i])){
G[i].push_back(j);
G[j].push_back(i);
}
}
}
}
void p(){
for (int i = 0 ;i < cc; i++){
for (int j = 0; j < 2 ; j++){
for (int k = 0 ; k < term[i][j].size(); k++)
printf("%d ",term[i][j][k]);
puts("");
}
puts("");
}
}
bool dfs(int u,int c){
color[u] = c;
vis[u] = 1;
term[cc][c-1].push_back(u);
for (int i=0;i < G[u].size();i ++){
int v = G[u][i];
if (v == u) continue;
if (color[v] && color[u] == color[v]) return false;
if (!color[v] && !dfs(v,3-c)) return false;
}
return true;
}
void print(int ans){
vector<int> term1,term2;
int t;
for (int i = cc - 1;i >= 0 ;i --){
if (d[i][ans-diff[i]+n]){
t = 0;
ans -= diff[i];
}else {
t = 1;
ans += diff[i];
}
for (int j = 0; j < term[i][t].size(); j++){
term1.push_back(term[i][t][j]);
}
for (int j = 0;j < term[i][t^1].size(); j++)
term2.push_back(term[i][t^1][j]);
}
printf("%d",term1.size());
for (int j = 0;j < term1.size(); j++)
printf(" %d",term1[j]);
puts("");
printf("%d",term2.size());
for (int j = 0;j < term2.size(); j++)
printf(" %d",term2[j]);
puts("");
}
void DP(){
d[0][n] = 1;
for (int i = 0;i < cc;i ++){
for (int j=-n ;j <= n;j ++){
if (d[i][j+n]){
d[i+1][j+diff[i]+n] = 1;
d[i+1][j-diff[i]+n] = 1;
}
}
}
// p();
for (int i = 0 ;i <= n;i ++){
if (d[cc][i+n]){
print(i);
return ;
}
if (d[cc][-i+n]){
print(-i);
return ;
}
}
}
void solve(){
int flag = 0;
cc = 0;
for (int i = 1;i <= n;i ++){
if (!color[i]){
if (!dfs(i,1)){
flag = 1;
break;
}
diff[cc] = term[cc][0].size() - term[cc][1].size();
cc++;
}
}
if (flag){
puts("No solution");
return ;
}
DP();
}
int main(){
int t;
//freopen("1.txt","r",stdin);
scanf("%d",&t);
while(t--){
input();
solve();
puts("");
}
return 0;
}
uva 1627 Team them up! 二分图 + 连通分量 + 背包
最新推荐文章于 2021-08-14 22:34:36 发布