LOJ2540 [PKUWC2018] 随机算法 【状压DP】

题目分析:

听说这题考场上能被$ O(4^n) $的暴力水过,难不成出题人是毕姥爷?

首先思考一个显而易见的$ O(n^2*2^n) $的暴力DP。一般的DP都是考虑最近的加入了哪个点,然后删除后递归进行状压DP。由于这道题的题目询问方式是反过来的,处理方式也反过来。

令$ f[n][S] $表示当前有$ S $这些点,期望这些点能够构成独立集大小为$ n $。正向的考虑选择了哪个点,并把与这个点有连边的所有点在集合内进行删除,令找到的新状态为$ f[n-1][P] $。我们把$ P $中的结点与$ S $中不在$ P $中的点进行标号拼接。写成语言就是$ f[n][S]+=f[n-1][P] * \binom{|S|-1}{|P|} * |P|! $ 由于对于$ S $的每一个点都需要转移一遍,时间复杂度就变成了$ O(n^2*2^n) $。虽然跑得不快,但是由于冗余状态较多,考场上一定比例的人使用这个算法通过了这个题。

现在来考虑把它优化到$ O(n*2^n) $。由于题目期望着你获得一个最大独立集,所以我们可以发现第一维是没有必要的。因为对于一个目标状态$ S $,我们如果知道$ S $对应的最大独立集的大小的话,那么我们必定是奔着这个大小而去的。现在我们用$ g[S] $来表示$ S $对应的最大独立集大小,那么这是一个普及组题目,枚举选点然后求max就行了。再对于$ f[S] $,求$ S $对应的最大独立集大小。首先记录$ g[S] $,然后找删除某个点后的集合变为了$ g[S]-1 $的就是我们想要的转移方案,同样采用带标号的拼接。因为我们没有了第一维的负担,所以时间复杂度骤降为了$ O(n*2^n) $.

ps:我终于会用letax数学公式啦。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3
 4 const int maxn = 25;
 5 const int mod = 998244353;
 6
 7 int n,m;
 8 int connect[maxn];
 9 int f[(1<<20)+5],g[(1<<20)+5],sz[(1<<20)+5];
10 int arr[(1<<20)+5],C[maxn][maxn],fac[25];
11
12 int fast_pow(int now,int pw){
13     if(pw == 1) return now;
14     int z = fast_pow(now,pw/2);
15     z = (1ll*z*z) % mod;
16     if(pw & 1) z = (1ll*z*now)%mod;
17     return z;
18 }
19
20 void read(){
21     scanf("%d%d",&n,&m);
22     for(int i=1;i<=m;i++){
23     int u,v; scanf("%d%d",&u,&v);
24     connect[u] |= (1<<v-1);
25     connect[v] |= (1<<u-1);
26     }
27     for(int i=1;i<=n;i++) connect[i] |= (1<<i-1);
28 }
29
30 void init(){
31     C[0][0] = 1;
32     for(int i=1;i<=n;i++){
33     C[i][0] = C[i][i] = 1;
34     for(int j=1;j<i;j++) C[i][j] = (C[i-1][j-1]+C[i-1][j])%mod;
35     }
36     for(int i=1;i<(1<<n);i++){
37     for(int j=0;j<n;j++) if((1<<j)&i) sz[i]++;
38     }
39     fac[0] = 1;
40     for(int i=1;i<=n;i++) fac[i] = (1ll*fac[i-1]*i)%mod;
41 }
42
43 void dfs(int now){
44     arr[now] = 1;
45     for(int i=1;i<=n;i++){
46     if(!((1<<i-1)&now)) continue;
47     int p = now - (now&connect[i]);
48     if(!arr[p]) dfs(p);
49     g[now] = max(g[now],g[p]+1);
50     }
51 }
52
53 void dfs2(int now){
54     arr[now] = 1;
55     for(int i=1;i<=n;i++){
56     if(!((1<<i-1)&now)) continue;
57     int kk = (now&connect[i]),p = now - kk;
58     if(g[p] != g[now]-1) continue;
59     if(!arr[p]) dfs2(p);
60     f[now]+=((1ll*C[sz[now]-1][sz[p]]*fac[sz[kk]-1])%mod)*f[p]%mod;
61     f[now] %= mod;
62     }
63 }
64
65 void work(){
66     init();
67     g[0] = 0; arr[0] = 1;
68     for(int i=1;i<(1<<n);i++) if(!arr[i]) dfs(i);
69     memset(arr,0,sizeof(arr));
70     f[0] = 1; arr[0] = 1;
71     dfs2((1<<n)-1);
72     int ans = f[(1<<n)-1];
73     ans = (1ll*fast_pow(fac[n],mod-2)*ans)%mod;
74     printf("%d",ans);
75 }
76
77 int main(){
78     read();
79     work();
80     return 0;
81 }

原文地址:https://www.cnblogs.com/Menhera/p/9064561.html

时间: 2024-10-11 20:30:00

LOJ2540 [PKUWC2018] 随机算法 【状压DP】的相关文章

算法复习——状压dp

状压dp的核心在于,当我们不能通过表现单一的对象的状态来达到dp的最优子结构和无后效性原则时,我们可能保存多个元素的有关信息··这时候利用2进制的01来表示每个元素相关状态并将其压缩成2进制数就可以达到目的····此时熟悉相关的位运算就很重要了····以下是常见的一些需要位运算方法 然后说实话状压dp其它方面就和普通dp差不多了···它不像数位区间树形那样或多或少好歹有自己一定套路或规律····如何想到转移方程和状态也就成了其最难的地方··· 例题: 1.Corn Fields(poj3254)

PKUWC2018 随机算法

给你一个$n$个点$m$条边的无向图,执行如下算法: 1.随机一个$1~n$的排列$P$ 2.从$P$中按顺序一个一个将点加进独立集$S$里,始终保证$S$是独立集(即如果当前点和当前集合里的某个点相邻,就不加了) 求最后得到的$S$是原图的一个最大独立集的概率 $50% n \leq 17$ $100% n \leq 20$ sol: 先考虑部分分吧 很暴力的状压dp,$F[S1][S2]$表示选了$S1$中的点,当前最大独立集是$S2$的方案数,最后除以$n!$就可以了 转移是枚举每个点,所

【BZOJ2064】分裂 状压DP

[BZOJ2064]分裂 Description 背景:和久必分,分久必和...题目描述:中国历史上上分分和和次数非常多..通读中国历史的WJMZBMR表示毫无压力.同时经常搞OI的他把这个变成了一个数学模型.假设中国的国土总和是不变的.每个国家都可以用他的国土面积代替,又两种可能,一种是两个国家合并为1个,那么新国家的面积为两者之和.一种是一个国家分裂为2个,那么2个新国家的面积之和为原国家的面积. WJMZBMR现在知道了很遥远的过去中国的状态,又知道了中国现在的状态,想知道至少要几次操作(

BZOJ1087:[SCOI2005]互不侵犯King(状压DP)

[SCOI2005]互不侵犯King Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. Input 只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N) Output 方案数. Sample Input 3 2 Sample Output 16 分析: 经典的状压DP题目,可我竟然调了很长时间都没对,后来发现是DP枚举范围错

(九度OJ)题目1338:角斗士(状压DP)

题目描述: 角斗士是古罗马奴隶社会的一种特殊身份的奴隶,他们的职责是在角斗场上进行殊死搏斗,为了人们提供野蛮的娱乐.他们的结局或是战死,或者由于表现突出赢得胜利而获得释放. 现在在角斗场里有N个待战的角斗士(1 <=N<=18),每天都将举行一场比赛,为了保持比赛的刺激性,每场比赛前才会在所有当前活着的角斗士之中随机选择两名进行上场拼杀.每场比赛的结束条件即为其中一名被杀死.当进行了N场比赛之后,最后存活的角斗士将被释放.而你将被赋予一个任务,计算出每名角斗士最终存活的概率.我们将提供角斗士之

HDU 1565&amp;1569 方格取数系列(状压DP或者最大流)

方格取数(2) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 6206    Accepted Submission(s): 1975 Problem Description 给你一个m*n的格子的棋盘,每个格子里面有一个非负数. 从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的

[BZOJ1087] [SCOI2005] 互不侵犯King (状压dp)

Description 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. Input 只有一行,包含两个数N,K ( 1 <=N <=9, 0 <= K <= N * N) Output 方案数. Sample Input 3 2 Sample Output 16 HINT Source Solution 状压$dp$就是把状态压缩成二进制数,利用二进制的位运算改进算法的一种方法

状压DP [HDU 1074] Doing Homework

Doing Homework Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 5472    Accepted Submission(s): 2311 Problem Description Ignatius has just come back school from the 30th ACM/ICPC. Now he has a lo

hlg2130一笔画【状压dp】

一笔画 Time Limit: 3000 MS Memory Limit: 32768 K Total Submit: 6(2 users) Total Accepted: 1(1 users) Rating:  Special Judge: No Description 小明喜欢画画,今天他在地上捡起了一张纸,这张纸上有好多个点.小明就想,如果我用铅笔一次性(中间不抬笔)把这些点连起来,那么我的铅笔需要在纸上走多长的路呢? Input 第一行,测试组数t. 每组第一行一个正整数N(1<=N<