需要稍加分析结论;还有一些小细节

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 Abecome 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, v ≤ n; u ≠ v), 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.


题目大意

给定一些关系(u,v)代表两个人是朋友。每次可以选择一个人i,使i的朋友都相互变成朋友。问最少需要选择多少人,使得所有的人都能够相互认识。

题目分析

一些初步的想法

一开始比较自然地会考虑完全子图的情况。

那么有一个想法是:能不能把这整个完全子图给看成一个人?既然他们互相认识,那么这样它连出的边可以等效吗?

但这个想法的问题在于,这个完全子图的关系“不可拓展”,也就是说在他们的朋友介绍新朋友时,新加进来的人并不能和其他人认识。

那么贪心?

考虑到新加进人的过程是不可逆的。那么能不能够贪心地每次选尽可能多的点呢?

这样似乎还没有找到反例。但是这个做法的潜在危险在于其复杂度基于答案大小,有可能会因为答案数过大而TLE/MLE.

 #include<bits/stdc++.h>
const int INF = 0x3f3f3f3f;
const int maxn = ; int n,m,ans,id;
struct node
{
int val,vis[maxn],pr[maxn],deg[maxn];
std::bitset<maxn> mp[maxn];
bool legal()
{
for (int i=; i<=n; i++)
if (mp[i].count() < n) return ;
return ;
}
void print()
{
for (int i=; i<=val; i++)
printf("%d ",pr[i]);
puts("");
}
}now,tmp,chg;
std::queue<node> q; int read()
{
char ch = getchar();
int num = ;
bool fl = ;
for (; !isdigit(ch); ch=getchar())
if (ch=='-') fl = ;
for (; isdigit(ch); ch=getchar())
num = (num<<)+(num<<)+ch-;
if (fl) num = -num;
return num;
}
int main()
{
freopen("party.in","r",stdin);
freopen("party.out","w",stdout);
n = read(), m = read(), now.val = , ans = INF;
for (int i=; i<=n; i++) now.mp[i][i] = ;
for (int i=; i<=m; i++)
{
int u = read(), v = read();
now.mp[u][v] = , now.mp[v][u] = ;
}
q.push(now);
while (q.size())
{
tmp = q.front(), q.pop();
if (tmp.legal()){
ans = tmp.val;
printf("%d\n",ans);
tmp.print();
break;
}
bool fnd = ;
while (!fnd){
id = ;
for (int i=; i<=n; i++)
if (!tmp.vis[i]){
tmp.deg[i] = tmp.mp[i].count();
if (tmp.deg[i] > tmp.deg[id]) id = i;
}
for (int i=; i<=n; i++)
if (tmp.deg[i]==tmp.deg[id]){
bool upd = ;
chg = tmp, chg.val++;
chg.vis[i] = , chg.pr[chg.val] = i;
for (int j=; j<=n; j++)
for (int k=; k<=n; k++)
if (j!=k&&chg.mp[i][j]&&chg.mp[i][k]&&!chg.mp[j][k])
chg.mp[k][j] = chg.mp[j][k] = , upd = ;
if (upd) fnd = , q.push(chg);
else tmp.vis[i] = ;
}
}
}
return ;
}

状压dp

注意到特殊的数据范围,考虑状压。

首先明确两个结论:

  1. 答案与合并顺序无关
  2. 对图$G=\{V,E\}$中一个完全子图$G'=\{V',E'\}$中的点$x\in V'$进行操作后,其所在的完全子图$G''=\{V'+V_x,E'+E_x\},E_x=\{x,V_x\}\in E$。换句话说就是操作集合内的点能使集合变大。

第一条是因为操作点$x$之后$\forall (x,y_i)$,$y_i$之间都存在边,那么下一步选取$y_i$时会把其他的$y_j$也都连在一起;第二条比较容易理解。

那么用$f[i]$表示$i$的二进制状态下,这些人相互认识的最小代价。

于是转移就是经典的状压转移;顺便再记录一下转移的位置就好了。

需要注意的细节是原图是否已经连通,那么这个就是预处理的时候再判断一下。

 #include<bits/stdc++.h>
const int maxn = ;
const int maxs = ;
const int INF = 0x3f3f3f3f; bool fnd;
int n,m,mx;
int s[maxn],f[maxs],fa[maxs],pr[maxs]; int read()
{
char ch = getchar();
int num = ;
bool fl = ;
for (; !isdigit(ch); ch=getchar())
if (ch=='-') fl = ;
for (; isdigit(ch); ch=getchar())
num = (num<<)+(num<<)+ch-;
if (fl) num = -num;
return num;
}
void fndScheme(int st)
{
if (fa[st]) fndScheme(fa[st]);
printf("%d ",pr[st]);
}
int main()
{
// freopen("party.in","r",stdin);
// freopen("party.out","w",stdout);
memset(f, 0x3f3f3f3f, sizeof f);
n = read(), m = read(), mx = (<<n)-;
for (int i=; i<n; i++) s[i+] = <<i;
for (int i=; i<=m; i++)
{
int x = read()-, y = read()-;
s[x+] |= <<y, s[y+] |= <<x;
}
fnd = ;
for (int i=; i<=n; i++)
{
f[s[i]] = , pr[s[i]] = i;
if (s[i]!=mx) fnd = ;
}
if (fnd){
puts("");
return ;
}
for (int st=; st<=mx; st++)
if (f[st]!=INF){
for (int i=; i<=n; i++)
if (((st>>(i-))&)&&(f[st]+ < f[st|s[i]])){
f[st|s[i]] = f[st]+;
fa[st|s[i]] = st;
pr[st|s[i]] = i;
}
}
printf("%d\n",f[mx]);
fndScheme(mx);
return ;
}

END

【状压dp】cf906C. Party的更多相关文章

  1. BZOJ 1087: [SCOI2005]互不侵犯King [状压DP]

    1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3336  Solved: 1936[Submit][ ...

  2. nefu1109 游戏争霸赛(状压dp)

    题目链接:http://acm.nefu.edu.cn/JudgeOnline/problemShow.php?problem_id=1109 //我们校赛的一个题,状压dp,还在的人用1表示,被淘汰 ...

  3. poj3311 TSP经典状压dp(Traveling Saleman Problem)

    题目链接:http://poj.org/problem?id=3311 题意:一个人到一些地方送披萨,要求找到一条路径能够遍历每一个城市后返回出发点,并且路径距离最短.最后输出最短距离即可.注意:每一 ...

  4. [NOIP2016]愤怒的小鸟 D2 T3 状压DP

    [NOIP2016]愤怒的小鸟 D2 T3 Description Kiana最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于(0,0)处,每次Kiana可 ...

  5. 【BZOJ2073】[POI2004]PRZ 状压DP

    [BZOJ2073][POI2004]PRZ Description 一只队伍在爬山时碰到了雪崩,他们在逃跑时遇到了一座桥,他们要尽快的过桥. 桥已经很旧了, 所以它不能承受太重的东西. 任何时候队伍 ...

  6. bzoj3380: [Usaco2004 Open]Cave Cows 1 洞穴里的牛之一(spfa+状压DP)

    数据最多14个有宝藏的地方,所以可以想到用状压dp 可以先预处理出每个i到j的路径中最小权值的最大值dis[i][j] 本来想用Floyd写,无奈太弱调不出来..后来改用spfa 然后进行dp,这基本 ...

  7. HDU 1074 Doing Homework (状压dp)

    题意:给你N(<=15)个作业,每个作业有最晚提交时间与需要做的时间,每次只能做一个作业,每个作业超出最晚提交时间一天扣一分 求出扣的最小分数,并输出做作业的顺序.如果有多个最小分数一样的话,则 ...

  8. 【BZOJ1688】[Usaco2005 Open]Disease Manangement 疾病管理 状压DP

    [BZOJ1688][Usaco2005 Open]Disease Manangement 疾病管理 Description Alas! A set of D (1 <= D <= 15) ...

  9. 【BZOJ1725】[Usaco2006 Nov]Corn Fields牧场的安排 状压DP

    [BZOJ1725][Usaco2006 Nov]Corn Fields牧场的安排 Description Farmer John新买了一块长方形的牧场,这块牧场被划分成M列N行(1<=M< ...

随机推荐

  1. linux查找工具(find,locate,whrers)

    find whereis locate 一.whereis whereis命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b).man说明文件(参数-m)和源代码文件(参数-s).如果省略参数, ...

  2. Luogu P3941 入阵曲【前缀和】By cellur925

    题目传送门 题目大意:给你一个\(n\)*\(m\)的矩阵,每个位置都有一个数,求有多少不同的子矩阵使得矩阵内所有数的和是\(k\)的倍数. 数据范围给的非常友好233,期望得到的暴力分:75分.前1 ...

  3. 程序员/开发人员的真实生活 (Gif 多图)

    往工作环境上传东西的时候: 没保存,就关了 IDE 的时候: 凌晨三点调代码的时候: 正则表达式返回了了预期结果的时候: 当老板告诉我,我那一直负责的模块失效了的时候: 刚修复了Bug,我给老板演示的 ...

  4. 线程池(1)ThreadPoolExecutor梳理

    使用默认的 thread factory创建ThreadPoolExecutor实例 public ThreadPoolExecutor(int corePoolSize, int maximumPo ...

  5. net core WebApi 使用Swagger

    Asp.net core WebApi 使用Swagger生成帮助页 最近我们团队一直进行.net core的转型,web开发向着前后端分离的技术架构演进,我们后台主要是采用了asp.net core ...

  6. CentOS,net core2 sdk nginx、supervisor、mysql

    CentOS下 .net core2 sdk nginx.supervisor.mysql环境搭建 作为.neter,看到.net core 2.0的正式发布,心里是有点小激动的,迫不及待的体验了一把 ...

  7. java Integer

    Java 中的数据类型分为基本数据类型和引用数据类型 int是基本数据类型,Integer是引用数据类型: Ingeter是int的包装类,int的初值为0,Ingeter的初值为null. 初始化 ...

  8. Exception in thread "main" java.lang.UnsupportedClassVersionError: com/google/common/base/Function : Unsupported major.minor version 52.0的解决办法(图文详解)

    不多说,直接上干货! 问题详情 Exception in thread "main" java.lang.UnsupportedClassVersionError: com/goo ...

  9. Java热启动

    1.在项目中的pom.xml中添加 <dependency> <groupId>org.springframework.boot</groupId> <art ...

  10. SpringMVC的基础配置及视图定位

    概要 记录一下搭建SpringMVC框架的步骤 视图定位也就是改变jsp在项目中的路径 一.新建javaweb项目springmvc1,在lib中导入jar包 此项目上传了GitHub,方便去下载ja ...