CodeForces 906C Party(状压dp)

参考:https://blog.csdn.net/u013534123/article/details/78887346

C. Party

time limit per test:1 second

memory limit per test:256 megabytes

input:standard input

output:standard output

Arseny likes to organize parties and invite people to it. However, not only friends come to his parties, but friends of his friends, friends of friends of his friends and so on. That's why some of Arseny's guests can be unknown to him. He decided to fix this issue using the following procedure.

At each step he selects one of his guests A, who pairwise introduces all of his friends to each other. After this action any two friends of A become friends. This process is run until all pairs of guests are friends.

Arseny doesn't want to spend much time doing it, so he wants to finish this process using the minimum number of steps. Help Arseny to do it.

Input

The first line contains two integers n and m (1 ≤ n ≤ 22; ) — the number of guests at the party (including Arseny) and the number of pairs of people which are friends.

Each of the next m lines contains two integers u and v (1 ≤ u, vnuv), which means that people with numbers u and v are friends initially. It's guaranteed that each pair of friends is described not more than once and the graph of friendship is connected.

Output

In the first line print the minimum number of steps required to make all pairs of guests friends.

In the second line print the ids of guests, who are selected at each step.

If there are multiple solutions, you can output any of them.

Examples

Input

5 6
1 2
1 3
2 3
2 5
3 4
4 5

Output

2
2 3 

Input

4 4
1 2
1 3
1 4
3 4

Output

1
1 

Note

In the first test case there is no guest who is friend of all other guests, so at least two steps are required to perform the task. After second guest pairwise introduces all his friends, only pairs of guests (4, 1) and (4, 2) are not friends. Guest 3 or 5 can introduce them.

In the second test case guest number 1 is a friend of all guests, so he can pairwise introduce all guests in one step.

 

 

        日常打cf……

        大致题意:给你一堆一对对的关系,然后每一个关系对代表两个人认识。然后你每次可以选择一个人i,让i认识的所有人都相互认识,即i把介绍自己所有的朋友给其他人。然后现在问你最少需要选择多少个这样的i,使得所有的人都相互认识。

        看到只有22的数据范围,很容易想到状态压缩,但是如何利用这个状态压缩却没那么好考虑。如果考虑状态压缩dp,显然无法表示连通的状态。于是考虑用搜索,22个点要么最后被选要么没有,做多2^22=400W+,在承受范围内。然后就是看如何处理这个关系了,对于每一个人,我们用一个22位的2进制数字表示他是否与其他的人有关系。对于一个人x,每次选择他的时候,枚举所有与其相连的点i,然后i的状态或上x的状态即可。这样子复杂度就是O(N*2^N),可以通过。具体见代码:

 错误代码:

直接枚举组合,会多一维(无法按照顺序累加)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define dprintf if (debug) printf
#define rep(i, j, k) for(int i=j; i<k; i++)
const int maxn = 25;
const int INF = 0x7fffffff; 
int cnt, n, m, u, v, ans;
int a[maxn], frd;
int main(){
	scanf("%d%d", &n, &m);
	rep(i, 0, m){
		scanf("%d%d", &u, &v);
		u--; v--;
		a[u] |= 1<<v;
		a[v] |= 1<<u;
	}
	rep(i, 0, n){
		a[i] |= 1<<i;
	}
	int already = 1;
	int tmp = 1<<n, minn = INF;
	rep(i, 0, n){
		if (a[i] != tmp-1) already = 0; 
	}
	if (already){
		puts("0");
		return 0;
	}
	rep(i, 0, tmp){
		cnt = 0; frd = 0;
		rep(j, 0, n){
			if (i & (1<<j)){
				cnt++;
				frd |= a[j];
			}
		}
		if (frd == tmp-1 && cnt<minn)
			minn = cnt, ans = i;
	}
	int tot = 0;
	rep(i, 0, n){
		if (ans & (1<<i)) tot++;
	}
	printf("%d\n", tot);
	rep(i, 0, n){
		if (ans & (1<<i)) printf("%d ", i+1);
	}
}

正确代码:

dp[i][j] 表示0..i的人在当前选择下选择后,第j个人的朋友

这样通过一定顺序下的dp,省了一维循环

#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define dprintf if (debug) printf
stack<int> stk, ans;
const int maxn = 25;
int n, m, u, v;
ll dp[maxn][maxn];
void dfs(int x){
	if (stk.size() >= ans.size()) return;
	if (x == n){
		for (int i=0; i<n; i++){
			if (dp[x][i] != (1<<n) - 1) return;
		}
		ans = stk; return;
	}
	memcpy(dp[x+1], dp[x], sizeof(dp[x]));
	dfs(x+1);
	for (int i=0; i<n; i++){
		if (dp[x][x] & (1<<i)) dp[x+1][i] |= dp[x][x];
	}
	stk.push(x); dfs(x+1); stk.pop();
}
int main(){
	scanf("%d%d", &n, &m);
	for (int i=0; i<n; i++){
		ans.push(i);
		dp[0][i] = 1<<i;
	}
	for (int i=0; i<m; i++){
		scanf("%d%d", &u, &v);
		u--; v--;
		dp[0][u]|=1<<v;
		dp[0][v]|=1<<u;
	}
	dfs(0);
	printf("%d\n", ans.size());
	while (!ans.empty()){
		printf("%d ", ans.top()+1);
		ans.pop();
	}
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值