题目:CF906C Party
啊,多么美妙的状压dp!我开始还以为是拓扑排序或者广搜之类的。。
我们用f[s]表示到达状态s可以使用的最小次数。那么f[s|a[i]]=Min(f[s|a[i]],f[s]+1),就是枚举每个人认识的人去更新答案。
然后怎么记录方案数呢?我们记一下这个状态是由哪个状态和哪个人拼起来更新的就可以了。
1 #include<stdio.h>
2 #include<string.h>
3 #define it register int
4 #define il inline
5 using namespace std;
6 const int N=(1<<22)+5;
7 int f[N],lst[N],ans[N],n,m,lim,a[N];
8 il void dfs(it x){
9 if(lst[x]) dfs(lst[x]);
10 printf("%d ",ans[x]);
11 }
12 il void fr(int &num){
13 num=0;char c=getchar();int p=1;
14 while(c<'0'||c>'9') c=='-'?p=-1,c=getchar():c=getchar();
15 while(c>='0'&&c<='9') num=num*10+c-'0',c=getchar();
16 num*=p;
17 }
18 int main(){
19 fr(n),fr(m);
20 if(m==n*(n-1)/2) return putchar('0'),0;
21 memset(f,127,sizeof(f)),lim=1<<n;
22 for(it i=1;i<=n;++i) a[i]=(1<<i-1);
23 for(it i=1,u,v;i<=m;++i) fr(u),fr(v),a[u]|=(1<<v-1),a[v]|=(1<<u-1);
24 for(it i=1;i<=n;++i) f[a[i]]=1,ans[a[i]]=i;
25 for(it s=0,x;s<lim;++s)
26 if(f[s]<2139062143)
27 for(it i=1;i<=n;++i)
28 if((s&(1<<i-1))&&f[(x=(s|a[i]))]>f[s]+1)
29 f[x]=f[s]+1,ans[x]=i,lst[x]=s;
30 printf("%d
",f[lim-1]);
31 dfs(lim-1);
32 return 0;
33 }
34 /*
35 f[s]:到达状态s所需要的最小步数
36 ans[x]:状态x是由ans[x]这个人更新的
37 lst[x]:状态x的上一个状态
38 */