1. 程式人生 > >LOJ2540 隨機演算法 狀壓DP

LOJ2540 隨機演算法 狀壓DP

傳送門


 

兩種$DP$:

①$f_{i,j}$表示前$i$次選擇,最大獨立集為$j$時達到最大獨立集的方案總數,轉移:$a.f_{i,j}+=f_{i+1,j+2^k}$(保證$k$加入後符合條件);$b.f_{i,j}+=f_{i+1,j} \times \text{現在可以放的不影響最大獨立集的點的數量}$

複雜度$O(2^nn^2)$而且似乎無法優化

  1 #include<bits/stdc++.h>
  2 //This code is written by Itst
  3 using namespace std;
  4 
  5
inline int read(){ 6 int a = 0; 7 bool f = 0; 8 char c = getchar(); 9 while(c != EOF && !isdigit(c)){ 10 if(c == '-') 11 f = 1; 12 c = getchar(); 13 } 14 while(c != EOF && isdigit(c)){ 15 a = (a << 3) + (a << 1
) + (c ^ '0'); 16 c = getchar(); 17 } 18 return f ? -a : a; 19 } 20 21 const int MOD = 998244353; 22 bool can[21][1 << 20] , edge[21][21] , choose[21]; 23 int dp[21][1 << 20] , cnt1[1 << 20] , N , M , maxN , logg2[1 << 21]; 24 25 inline int poww(long long
a , int b){ 26 int times = 1; 27 while(b){ 28 if(b & 1) 29 times = times * a % MOD; 30 a = a * a % MOD; 31 b >>= 1; 32 } 33 return times; 34 } 35 36 void dfs(int now , int size){ 37 if(N - now + size <= maxN) 38 return; 39 if(now == N){ 40 maxN = size; 41 return; 42 } 43 dfs(now + 1 , size); 44 for(int i = 0 ; i < now ; ++i) 45 if(choose[i] && edge[i][now]) 46 return; 47 choose[now] = 1; 48 dfs(now + 1 , size + 1); 49 choose[now] = 0; 50 } 51 52 int main(){ 53 #ifndef ONLINE_JUDGE 54 freopen("2540.in" , "r" , stdin); 55 //freopen("2540.out" , "w" , stdout); 56 #endif 57 N = read(); 58 logg2[0] = -1; 59 for(int i = 1 ; i < (1 << N) ; ++i){ 60 cnt1[i] = cnt1[i - (i & -i)] + 1; 61 logg2[i] = logg2[i >> 1] + 1; 62 } 63 for(M = read() ; M ; --M){ 64 int a = read() - 1 , b = read() - 1; 65 edge[a][b] = edge[b][a] = 1; 66 } 67 dfs(0 , 0); 68 for(int i = 0 ; i < N ; ++i){ 69 can[i][0] = 1; 70 for(int j = 1 ; j < (1 << N) ; ++j) 71 if(!(j & (1 << i))) 72 if(can[i][j - (j & -j)]){ 73 int minN = logg2[j & -j]; 74 if(!edge[i][minN]) 75 can[i][j] = 1; 76 } 77 } 78 for(int i = 1 ; i < (1 << N) ; ++i) 79 if(cnt1[i] == maxN) 80 dp[N][i] = 1; 81 for(int i = N - 1 ; i ; --i) 82 for(int j = 1 ; j < (1 << N) ; ++j) 83 if(cnt1[j] <= i && cnt1[j] <= maxN){ 84 int cnt = 0; 85 for(int p = ((1 << N) - 1) ^ j ; p ; p ^= p & -p){ 86 int k = logg2[p & -p]; 87 if(can[k][j]) 88 dp[i][j] = (dp[i][j] + dp[i + 1][j | (1 << k)]) % MOD; 89 else 90 ++cnt; 91 } 92 dp[i][j] = (dp[i][j] + 1ll * dp[i + 1][j] * (cnt + cnt1[j] - i)) % MOD; 93 } 94 long long sum = 0 , ans = 1; 95 for(int i = 0 ; i < N ; ++i) 96 sum = (sum + dp[1][1 << i]) % MOD; 97 for(int i = 2 ; i <= N ; ++i) 98 ans = ans * i % MOD; 99 cout << sum * poww(ans , MOD - 2) % MOD; 100 return 0; 101 }
View Code

②$f_{j}$表示當前已經無法選擇(即已經選擇和與已經選擇的點相鄰的點)的集合為$j$時、獨立集取到最大的方案數,設$w_i$表示與$i$相鄰(包括$i$)的點集,則有轉移:$f_{S \cup w_i}+=f_{S} \times P_{N - |S| - 1}^{|S| - |S \cap w_i| - 1}$,記得要滿足最大獨立集大小要是最大的。

複雜度$O(2^nn)$

 1 #include<bits/stdc++.h>
 2 //This code is written by Itst
 3 using namespace std;
 4 
 5 inline int read(){
 6     int a = 0;
 7     bool f = 0;
 8     char c = getchar();
 9     while(c != EOF && !isdigit(c)){
10         if(c == '-')
11             f = 1;
12         c = getchar();
13     }
14     while(c != EOF && isdigit(c)){
15         a = (a << 3) + (a << 1) + (c ^ '0');
16         c = getchar();
17     }
18     return f ? -a : a;
19 }
20 
21 const int MOD = 998244353;
22 long long dp[1 << 21] , maxN[1 << 21] , cnt1[1 << 21] , need[21] , jc[21] = {1} , ny[21] = {1} , N , M;
23 
24 inline int poww(long long a , int b){
25     int times = 1;
26     while(b){
27         if(b & 1)
28             times = times * a % MOD;
29         a = a * a % MOD;
30         b >>= 1;
31     }
32     return times;
33 }
34 
35 int main(){
36 #ifndef ONLINE_JUDGE
37     freopen("2540.in" , "r" , stdin);
38     //freopen("2540.out" , "w" , stdout);
39 #endif
40     N = read();
41     for(int i = 1 ; i < 1 << N ; ++i)
42         cnt1[i] = cnt1[i - (i & -i)] + 1;
43     jc[1] = ny[1] = 1;
44     for(int i = 2 ; i <= N ; ++i)
45         jc[i] = jc[i - 1] * i % MOD;
46     ny[N] = poww(jc[N] , MOD - 2);
47     for(int i = N - 1 ; i > 1 ; --i)
48         ny[i] = ny[i + 1] * (i + 1) % MOD;
49     for(int i = 0 ; i < N ; ++i)
50         need[i] = 1 << i;
51     for(M = read() ; M ; --M){
52         int a = read() - 1 , b = read() - 1;
53         need[a] |= 1 << b;
54         need[b] |= 1 << a;
55     }
56     dp[0] = 1;
57     for(int i = 0 ; i + 1 < (1 << N) ; ++i)
58         for(int j = 0 ; j < N ; ++j)
59             if(!(i & (1 << j)) && maxN[i] + 1 >= maxN[i | need[j]]){
60                 if(maxN[i] + 1 > maxN[i | need[j]]){
61                     maxN[i | need[j]] = maxN[i] + 1;
62                     dp[i | need[j]] = 0;
63                 }
64                 dp[i | need[j]] = (dp[i | need[j]] + dp[i] * jc[N - cnt1[i] - 1] % MOD * ny[N - cnt1[i] - 1 - (cnt1[need[j]] - cnt1[i & need[j]] - 1)]) % MOD;
65             }
66     cout << dp[(1 << N) - 1] * ny[N] % MOD;
67     return 0;
68 }
View Code