Poj 1112 Team Them Up!

题意:现在有一群人,告诉你每个人都认识哪些人,让你将这一群人分成两组,其中每一组中的每个人都相互认识,并且使得两组中的人数尽量相近。问你是否能分成这样两组,如果不能输出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!

时间: 2024-10-29 19:11:12

Poj 1112 Team Them Up!的相关文章

Poj 1112 Rebuilding Roads(树形DP+背包)

题意:给你由N个点构成一颗树,问要孤立出一个有P个节点的子树最少需要删除多少条边.N的范围最大为150 N的范围不大,很容易想到在树上面做背包.把每个节点都看成一个背包,然后把每个儿子节点都看成是一组物品.为什么是一组呢,那是因为假设以儿子为根的节点的子树有S个节点,那么就有S+1种情况,要么将这整棵子树舍弃,要么从这个子树中取1-S个节点. 设f[i][j]为以i为根节点的子树,孤立出以i为根节点,一共含有j个节点的子树最少需要删除的边数(不包括删除i和他父亲的连接的那条边(假设i不是根节点)

【POJ 1112】Team Them Up!(二分图染色+DP)

Description Your task is to divide a number of persons into two teams, in such a way, that: everyone belongs to one of the teams; every team has at least one member; every person in the team knows every other person in his team; teams are as close in

poj 2038 Team Rankings 枚举排列

//poj 2038 //sep9 #include <iostream> #include <algorithm> using namespace std; char s[128][8]; int count(char s1[],char s2[]) { int cnt=0; for(int i=0;i<5;++i) for(int j=i+1;j<5;++j){ int k; for(k=0;k<5;++k) if(s1[i]==s2[k]) break; f

POJ 2259 Team Queue 数据结构 队列

Team Queue Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 3282   Accepted: 1188 Description Queues and Priority Queues are data structures which are known to most computer scientists. The Team Queue, however, is not so well known, thoug

POJ 2259 Team Queue(STL队列)

转自:https://www.cnblogs.com/yjlblog/p/7056746.html 题目背景 队列和优先级队列是大多数计算机科学家都知道的数据结构.但是团队队列却不被人熟知,尽管在生活中经常出现.比如,午餐时间的食堂门口的队列就是一个团队队列.在一个团队队列中,每个元素属于一个团队.如果一个元素进入一个队列,它首先从头到尾地搜寻这个队列--检查是否它的队友(在同一个团队称之为队友)也在这个队列里.如果有,它就排在它队友的后面(:-D就是插队咯~~).如果没有,它就排在整个队列的最

queue POJ 2259 Team Queue

题目传送门 题意:先给出一些小组成员,然后开始排队.若前面的人中有相同小组的人的话,直接插队排在同小组的最后一个,否则只能排在最后面.现在有排队和出队的操作. 分析:这题关键是将队列按照组数分组,用另外一个队列保存组的序号,当该组里没有人了才换下一组.很好的一道题. 收获:队列的灵活运用 代码: /************************************************ * Author :Running_Time * Created Time :2015/9/9 星期三

ACM总结——dp专辑(转)

感谢博主——      http://blog.csdn.net/cc_again?viewmode=list       ----------  Accagain  2014年5月15日 动态规划一直是ACM竞赛中的重点,同时又是难点,因为该算法时间效率高,代码量少,多元性强,主要考察思维能力.建模抽象能力.灵活度. 本人动态规划博客地址:http://blog.csdn.net/cc_again/article/category/1261899 ***********************

(转)dp动态规划分类详解

dp动态规划分类详解 转自:http://blog.csdn.NET/cc_again/article/details/25866971 动态规划一直是ACM竞赛中的重点,同时又是难点,因为该算法时间效率高,代码量少,多元性强,主要考察思维能力.建模抽象能力.灵活度. ****************************************************************************************** 动态规划(英语:Dynamic programm

这个是转的。学完就删_(:з」∠)_

原文链接:http://blog.csdn.net/cc_again/article/details/25866971 好多dp:http://blog.csdn.net/cc_again/article/category/1261899 一.简单基础dp 这类dp主要是一些状态比较容易表示,转移方程比较好想,问题比较基本常见的.主要包括递推.背包.LIS(最长递增序列),LCS(最长公共子序列),下面针对这几种类型,推荐一下比较好的学习资料和题目. 1.递推: 递推一般形式比较单一,从前往后,