【原题】
【题目翻译】
Arseny喜欢组织派对并邀请他的朋友们参加。然而,不仅朋友来参加他的聚会,还有他朋友的朋友,他朋友的朋友的朋友等等。所以Arseny的有一部分客人可能不了解他。他决定使用以下程序解决此问题。
在每一步,他都选择了一位客人A,成对地向A介绍了他的所有朋友。在完成这一步之后,他的任意两个朋友也会互相成为朋友。一直重复这个步骤到所有客人都互相为朋友为止。
Arseny不想花太多时间去完成这件事,所以他想用最少的步骤完成这个过程。Arseny需要你来帮帮她做到这一点。
【输入格式】
第一行包含两个整数n和m(0<=n<=22,0<=m<=n(n-1)/2)。n为派对上的客人人数(包括Arseny),m为Arseny朋友的人数。
接下来m行,每行包含两个整数u你和v,代表u和v在派对前就已经是朋友。保证每对朋友只会出现一次,并且友谊图表是相互关联的。
【输出格式】
在第一行输出所有的客人都成为朋友所需的最少步骤数。
在第二行输出每个步骤中选择的客人编号。
如果有多个解决方案,您可以输出任意一个解决方案。
S a m p l e I n p u t Sample~~Input Sample Input
5 6
1 2
1 3
2 3
2 5
3 4
4 5
S a m p l e O u t p u t Sample~~Output Sample Output
2
2 3
【题意分析】
n<=22提示是个状压题其实也是状压题
压缩状态:1表示和这一位上的人成为朋友,0还不是
输入时预处理好每个人和其他人的朋友关系存在state[i]
里
注意特判:若一开始每个人都互相是朋友,直接输出0,不特判会输出一些诡异东西
然后我们开始dp
枚举各个状态,如果两个人已经认识,并且扩展关系后更优,那么就扩展关系,保存答案路径。
输出时根据保存的路径反推上去即可
Code:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#define MAXN 7000000
using namespace std;
int state[30], dp[MAXN], father[MAXN], ans[MAXN], n, m;
bool flag = 0;
void write (int now) {
printf ("%d ", ans[now]);
if (father[now]) write (father[now]);
}
int main () {
scanf ("%d%d", &n, &m); int lim = (1 << n) - 1;
for (register int i = 1; i <= n; i++) state[i] = 1 << (i - 1);
for (register int i = 1; i <= m; i++) {
int u, v; scanf ("%d%d", &u, &v);
state[u] |= 1 << (v - 1), state[v] |= 1 << (u - 1);
}
memset (dp, 0x3f3f3f, sizeof dp);
for (register int i = 1; i <= n; i++) {
dp[state[i]] = 1, ans[state[i]] = i;
if (state[i] != lim) flag = 1;
}
if (! flag) {
puts ("0"); return 0;
}
for (register int i = 1; i <= lim; i++) {
if (dp[i] == 0x3f3f3f) continue;
for (register int j = 1; j <= n; j++)
if (((i >> (j - 1)) & 1) && dp[i] + 1 < dp[i | state[j]]) {
dp[i | state[j]] = dp[i] + 1;
father[i | state[j]] = i, ans[i | state[j]] = j;
}
}
printf ("%d\n", dp[lim]); write (lim);
return 0;
}