题意:现在有一群人,告诉你每个人都认识哪些人,让你将这一群人分成两组,其中每一组中的每个人都相互认识,并且使得两组中的人数尽量相近。问你是否能分成这样两组,如果不能输出No
Solution ,否则输出人数最相近的方案。
注意你认识我不代表我认识你,组中的每一个人都必须是相互认识的。
首先建立由人和人认识关系构成的有向图,然后将其转化成一张无向图,如果两个点之间的边不是双向的,等于没有,所以就将其删去,保留双向边。然后对这个无向图求一次补图,形成的补图可能有多个联通块,并且位于不同联通块中的点是肯定可以放在一个组内的。
之后对于每个联通块,做一次二分图染色判定,判断其是否可以构成一张二分图。为什么要这么做,因为每个联通块中相邻的点是肯定不能放到一个组当中的,所以要对联通块做一次01染色,看看是否可以把当前的联通块分成两组,每一组中的人都不是相邻的。如果做完染色后发现该联通块不能构成一个二分图,那么肯定是No
Solution,因为所有人都必须不是属于组1就是属于组2
做染色的同时统计每个联通块中奇点和偶点的数量,然后就可以来做背包了。建立一个容量为n/2的背包,对于每个联通块,我可以取里面所有的奇点也可以取里面所有的偶点,最后递归输出结果。
#include <cstdio>
#include <sstream>
#include <fstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <cctype>
#include <ctime>
#include <set>
#include <climits>
#include <vector>
#include <queue>
#include <stack>
#include <cstdlib>
#include <cmath>
#include <string>
#include <list>#define INPUT_FILE "in.txt"
#define OUTPUT_FILE "out.txt"using namespace std;
typedef long long LL;
const int INF = INT_MAX / 2;void setfile() {
freopen(INPUT_FILE,"r",stdin);
freopen(OUTPUT_FILE,"w",stdout);
}const int maxn = 105;
struct Node {
int cnt,e[maxn],col;
};Node node[maxn];
bool know[maxn],conflict,team1[maxn],vis[maxn];
bool g[maxn][maxn],dp[maxn * 2][maxn * 2];
int n,sz[maxn],sz_zero[maxn],sz_one[maxn],rcnt;
int pid[maxn][maxn];//染色
void dfs(int now,int id) {
if(node[now].col == 0) sz_zero[id]++;
else sz_one[id]++;
pid[id][sz[id]++] = now;
for(int i = 0;i < node[now].cnt;i++) {
int nowid = node[now].e[i];
if(node[nowid].col == -1) {
node[nowid].col = node[now].col ^ 1;
dfs(nowid,id);
} else {
if(node[nowid].col == node[now].col) {
conflict = true;
}
}
}
}//统计联通块并且进行染色
void judge() {
rcnt = 0;
for(int i = 1;i <= n;i++) {
if(node[i].col == -1) {
node[i].col = 0;
rcnt++;
dfs(i,rcnt);
}
}
}//将第i个联通块中颜色为col的点加入组中
void addteam(int i,int col) {
vis[i] = true;
if(node[i].col == col) {
team1[i] = true;
}
for(int j = 0;j < node[i].cnt;j++) {
int chid = node[i].e[j];
if(vis[chid] == false) {
addteam(chid,col);
}
}
}//输出答案
void print_path(int i,int now) {
if(i == 0) return;
if(dp[i - 1][now - sz_zero[i]]) {
memset(vis,0,sizeof(vis));
addteam(pid[i][0],0);
print_path(i - 1,now - sz_zero[i]);
} else {
memset(vis,0,sizeof(vis));
addteam(pid[i][0],1);
print_path(i - 1,now - sz_one[i]);
}
}void work() {
dp[0][0] = true;
//背包
for(int i = 1;i <= rcnt;i++) {
for(int j = 0;j <= n;j++) {
if(dp[i - 1][j] == true) {
dp[i][j + sz_zero[i]] = true;
dp[i][j + sz_one[i]] = true;
}
}
}
int val = -1;
for(int j = n / 2;j >= 1;j--) {
if(dp[rcnt][j] == true) {
val = j; break;
}
}
if(val == -1) {
puts("No solution");
} else {
print_path(rcnt,val);
printf("%d",val);
for(int i = 1;i <= n;i++) if(team1[i]) printf(" %d",i);
printf("\n%d",n - val);
for(int i = 1;i <= n;i++) if(!team1[i]) printf(" %d",i);
putchar(‘\n‘);
}
}int main() {
int tmp;
while(scanf("%d",&n) != EOF) {
memset(node,0,sizeof(node));
memset(sz,0,sizeof(sz));
memset(sz_zero,0,sizeof(sz_zero));
memset(sz_one,0,sizeof(sz_one));
memset(dp,0,sizeof(dp));
memset(team1,0,sizeof(team1));
memset(g,0,sizeof(g));
//输入并且建立补图
for(int i = 1;i <= n;i++) {
while(scanf("%d",&tmp),tmp) {
g[i][tmp] = true;
}
node[i].col = -1;
}for(int i = 1;i <= n;i++) {
for(int j = 1;j <= n;j++) {
if(!(g[i][j] && g[j][i]) && i != j) {
node[i].e[node[i].cnt++] = j;
}
}
}//染色并且统计联通块
conflict = false;
judge();
if(conflict) {
puts("No solution");
} else {
work();
}
}
return 0;
}
Poj 1112 Team Them Up!