题目
原题意比较难读,简单用自己的话翻译一下
给定一个n(n<=20)个点,m(n-1<=m<=C(n,2))条边,无自环、无重边、连通的无向图,
Ada从u找v的路径的流程,可以看做是以下这个函数
dfs(u):
1. 判断v是不是u的直连点,是的话,结束流程,返回u->v
2. 若第一步没找到,则从u走到a(u)上,再dfs(a[u])
若找到,函数返回u到v的路径,即dfs(u)=u->a(u)+dfs(a[u])
对于每个点u,构造a(u),要求u-a(u)的边在图上存在(有点竞赛图定向的意思)
使得从任意一个点u出发找v,都能找到,即函数不会死循环,输出a数组
思路来源
乱搞AC
题解
Codeforces Beta Round #11 D. A Simple Task(状压dp/无向图简单环的个数)_Code92007的博客-CSDN博客
可以发现,从任意一个点u出发,要找的点v,要么在u->a(u)的路径上,要么是a(u)的相邻点
这等价于,找到一个无向图简单环,使得所有点到环的距离不超过1,
可证充要性,如果存在v的距离>1则u找不到v,如果不存在的话所有点都能找到
让在环上的点形成一个环,不在环上的点往环上指即可
这里用到了完全图找环的技巧,即相同的环从环上最小点出发
原题中会减去环长为2的环,并答案除以2,因为顺逆时针各会统计一次,
本题中,环长为2的点也是答案,并且找到一个合法的环即可
做法是O(2^20*20*20)的,因为无自环重边,20*20实际是C(20,2)=190,2s可以通过
pre记录了一下转移前驱,can记录了一下当前状态dis<=1的可达点,是全集则表明合法
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
#define pb push_back
const int N=20,M=1<<20;
ll dp[M][N],can[M],ans;//dp[i][j]表示当前从lowbit(i)出发,访问了i内的点,且到达j的简单路径数
P pre[M][N];
int n,m,u,v,all,res[N];
vector<int>e[N];
int main(){
memset(res,-1,sizeof res);
scanf("%d%d",&n,&m);
for(int i=0;i<n;++i){
can[1<<i]|=(1<<i);
}
for(int i=1;i<=m;++i){
scanf("%d%d",&u,&v);
u--;v--;
can[1<<u]|=(1<<v);
can[1<<v]|=(1<<u);
e[u].pb(v);e[v].pb(u);
}
for(int i=0;i<n;++i){
dp[1<<i][i]=1;
}
int up=1<<n;
for(int i=1;i<up;++i){
for(int j=0;j<n;++j){
if(i>>j&1){
can[i]=can[i^(1<<j)]|can[1<<j];
break;
}
}
}
for(int i=1;i<up;++i){
for(int j=0;j<n;++j){
if(!dp[i][j])continue;
for(int k=0;k<e[j].size();++k){
int v=e[j][k];
if((i&(-i))>(1<<v))continue;
if(i&(1<<v)){
if((i&(-i))==(1<<v)){
if(can[i]==up-1){
//printf("i:%d j:%d\n",i,j);
puts("Yes");
//printf("v:%d j:%d\n",v,j);
res[v]=j;
for(int x=i,y=j,px,py;x;x=px,y=py){
px=pre[x][y].first,py=pre[x][y].second;
if(!px)break;
res[y]=py;
//printf("x:%d y:%d px:%d py:%d\n",x,y,px,py);
}
for(int l=0;l<n;++l){
if(res[l]==-1){
for(auto &v:e[l]){
if(i&(1<<v)){
res[l]=v;
break;
}
}
}
}
for(int l=0;l<n;++l){
printf("%d%c",res[l]+1," \n"[l==n-1]);
}
return 0;
}
}
}
else{
dp[i|(1<<v)][v]+=dp[i][j];
pre[i|(1<<v)][v]=P(i,j);
}
}
}
}
puts("No");
return 0;
}