题意:
给你n个人,告诉你谁认识谁。要求将他们分成两组,每组的人相互都认识,且每组人数尽量接近(人数的差值最小)。
输入:第一行输入有几个人,第i行表示第i-1个人认识那些人,0表示结束。
输出:共两行,第i行第一个数表示i组有多少个人,然后依次输出这几个人,第二行同理。
Sample Input
5
2 3 5 0
1 4 5 3 0
1 2 5 0
1 2 3 0
4 3 2 1 0
Sample Output
3 1 3 5
2 2 4
思路:
先抽象成二分图,然后看能不能二分,如果不能直接输出,如果能,就抽象成可以dp的问题,用dp找差值最小的解。
抽象过程:
对于每一个节点,都有跟他相互连接的一堆节点,组成一个强连通分量,然后给这个强连通分量染色。问题就抽象成了这样,
有t个物品,每个物品有u价值和v价值,同时你可以选择将物品的u价值和v价值可以互换(swap(u,v)),对于每种物品,你都可以选择换或者不换。su = u价值的和,sv等于v价值的和,求abs(su-sv)最小时,所有物品换了或者没换。
初步定义dp[i]表示前i个物品的最小abs(su-sv)
一般的求最优解的dp都有一下思路:这一次的最优解由前几次的最优解构成。但是这个dp不一样,当前最优解可能是上一次的次优解得到或者是上一次的第三优解,所以这种dp无法写出状态转移方程。
再次定义dp[i][j]表示前i种物品能否构成差值为j的解,
这样定义的话,可行解可以推出可行解,状态转移方程就很明显了。
下面直接上代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <iomanip>
#include <string>
#include <algorithm>
#include <stack>
#include <queue>
#include <set>
#include <vector>
#include <map>
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define uint unsigned int
#define l(x) ((x)<<1)
#define r(x) ((x)<<1|1)
#define lowbit(x) ((x)&(-(x)))
#define ms(a,b) memset(a,b,sizeof(a))
#define NSYNC std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
using namespace std;
const int maxn = 110;
int n;//有多少个节点
int total;//有多少个强连通分量
int g[maxn][maxn];//i节点和j节点是否相连
int color[maxn];//第i个节点的值(颜色)是多少
int col[maxn][3];//第i个分量里值为j的节点有几个
int mark[maxn][3][maxn];//第i个分量里值为j的第k个节点是谁
int f[maxn][maxn * 2];//前i个连通分量能不能构成差值为j
int path[maxn][maxn * 2];//用于记录最优解是怎么构成的
int dfs(int u) {
for (int i = 1; i <= n; i++) {
if (i != u && (!g[u][i] || !g[i][u])) {
if (color[i] == color[u]) return 0;
if (!color[i]) {
color[i] = 3 - color[u];
mark[total][color[i]][col[total][color[i]]++] = i;
if (!dfs(i))return 0;
}
}
}
return 1;
}
int main() {
scanf("%d", &n);
int tem, a; ms(color, 0);
for (int i = 1; i <= n; i++)
while (scanf("%d", &tem) && tem)
g[i][tem] = 1;
total = 0;
for (int i = 1; i <= n; i++)
if (!color[i]) {
total++;
color[i] = 1;
mark[total][1][col[total][1]++] = i;
if (!dfs(i)) {
printf("No solution\n");
return 0;
}
}
int a1, a2; ms(f, 0);
f[1][col[1][1] - col[1][2] + n] = 1;
f[1][col[1][2] - col[1][1] + n] = 1;
path[1][col[1][1] - col[1][2] + n] = 1;
path[1][col[1][2] - col[1][1] + n] = 2;
for (int i = 2; i <= total; i++) {
a = col[i][1] - col[i][2];
for (int j = 0; j <= n * 2; j++) {
if ((j - a) >= 0 && (j - a) <= 2 * n&&
f[i - 1][j - a] == 1) {
f[i][j] = 1;
path[i][j] = 1;
}
else if ((j + a) >= 0 && (j + a) <= 2 * n&&
f[i - 1][j + a] == 1) {
f[i][j] = 1;
path[i][j] = 2;
}
}
}
int i, j;
for (i = n; i <= 2 * n; i++)if (f[total][i])break;
for (j = n - 1; j >= 0; j--)if (f[total][j])break;
int ans = (i - n) < (n - j) ? i : j;
int tro = ((n - (ans - n)) / 2) + (ans - n);
printf("%d", tro);
int loc;
tem = ans;
for (int i = total; i >= 1; i--) {
loc = path[i][tem];
for (int j = 0; j < col[i][loc]; j++)
printf(" %d", mark[i][loc][j]);
tem = tem - (col[i][loc] - col[i][3 - loc]);
}
tem = ans;
printf("\n");
printf("%d", n - tro);
for (int i = total; i >= 1; i--) {
loc = 3 - path[i][tem];
for (int j = 0; j < col[i][loc]; j++)
printf(" %d", mark[i][loc][j]);
tem = tem - (col[i][3 - loc] - col[i][loc]);
}
printf("\n");
return 0;
}