[LOJ#2540][PKUWC2018]随机算法(概率DP)

场上数据很水,比较暴力的做法都可以过90分以上,下面说几个做法。

1. 暴力枚举所有最大独立集,对每个独立集分别DP。复杂度玄学,但是由于最大独立集并不多,所以可以拿90.

2. dp[S][k]表示考虑到排列的第k位,当前独立集为S的方案数,枚举第k+1位,根据是否与S相连转移到dp[S][k+1]或dp[S | a[k+1]][k+1]。$O(n^22^n)$

3. dp[S]表示排列的状态为S时的正确率,mx[S]表示排列状态为S时能得到的最大独立集大小,考虑转移,枚举排列里最后一个在独立集中的点i∈S,从S中删去所有与i相连的点得到S',若mx[S]<mx[S']+1则更新mx[S],dp[S]清零,否则累加。注意到每个排列都是等概率出现的,所以最后直接除以|S|即可。 $O(n2^n)$

方法一:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 5 #define ll long long
 6 using namespace std;
 7 
 8 const int N=1<<22,mod=998244353;
 9 ll n,m,x,y,s[25],p[25],f[N][25],cnt,mx,v[N],num[N],t[N],ans,o[N];
10 
11 int main(){
12     freopen("walk.in","r",stdin);
13     freopen("walk.out","w",stdout);
14     scanf("%lld%lld",&n,&m);
15     p[1]=1; rep(i,2,n) p[i]=p[i-1]<<1;
16     rep(i,1,m) scanf("%lld%lld",&x,&y),s[x]|=p[y],s[y]|=p[x];
17     cnt=(1<<n)-1; f[0][0]=1;
18     rep(i,0,cnt){
19         ll tmp=0; v[i]=1;
20         rep(j,1,n) if ((i&p[j])&&(s[j]&i)) v[i]=0;
21         if (v[i]){
22             rep(j,1,n) if (i&p[j]) tmp++,t[i]|=s[j];
23             num[i]=tmp; mx=max(mx,tmp);
24             tmp=0;
25             rep(j,1,n) if (t[i]&p[j]) tmp++;
26             o[i]=tmp;
27         }
28     }
29     rep(i,0,cnt) if (v[i])
30         rep(j,0,o[i]){
31             if (j!=o[i]) f[i][j+1]=(f[i][j+1]+f[i][j]*(o[i]-j))%mod;
32             rep(k,1,n) if (!(i&p[k])&&!(p[k]&t[i])) f[i|p[k]][j]=(f[i|p[k]][j]+f[i][j])%mod;
33             if (num[i]==mx && j==o[i]) ans=(ans+f[i][j])%mod;
34         }
35       printf("%lld\n",ans);
36       return 0;
37 }

方法二:

 1 #include<iostream> 
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<algorithm>
 7 using namespace std;
 8 int read()
 9 {
10     int x=0,f=1;char c=getchar();
11     while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
12     while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
13     return x*f;
14 }
15 #define P 998244353
16 #define N 21
17 #define t (1<<n)
18 int n,m;
19 long long ans=0;
20 bool flag[1<<(N-1)];
21 int s[1<<(N-1)],w[N],v[1<<(N-1)],cnt[1<<(N-1)],tot[1<<(N-1)],f[21][1<<(N-1)],maximum=1;
22 int main()
23 {
24     freopen("walk.in","r",stdin);
25     freopen("walk.out","w",stdout);
26     n=read(),m=read();
27     for (int i=1;i<=n;i++) w[i]=1<<(i-1),s[w[i]]=w[i];
28     for (int i=1;i<=m;i++) 
29     {
30         int x=read(),y=read();
31         s[w[x]]|=w[y],s[w[y]]|=w[x];
32     }
33     flag[0]=1;
34     for (int i=0;i<t;i++)
35     if (flag[i])
36         for (int j=1;j<=n;j++)
37         if (!(w[j]&s[i])) 
38         {
39             flag[i|w[j]]=1,s[i|w[j]]=s[i]|s[w[j]],cnt[i|w[j]]=cnt[i]+1;
40             if (cnt[i]>=maximum) maximum=cnt[i|w[j]];
41         }
42     for (int i=0;i<t;i++) 
43     {
44         s[i]=(~s[i])&(t-1);
45         register int k=s[i];
46         while (k) k^=k&-k,tot[i]++;
47         v[i]=i&-i;
48     }
49     f[0][0]=1;
50     for (register int i=0;i<n;i++)
51         for (register int j=0;j<t;j++)
52         if (f[i][j]) 
53         {
54             for (register int k=s[j];k;k^=v[k])
55             f[i+1][j|v[k]]=(f[i+1][j|v[k]]+f[i][j])%P;
56             f[i+1][j]=(1ll*f[i][j]*(n-i-tot[j])+f[i+1][j])%P;
57         }
58     for (int i=0;i<t;i++) if (cnt[i]==maximum) ans=(ans+f[n][i])%P;
59     cout<<ans;
60     fclose(stdin);fclose(stdout);
61     return 0;
62 }

方法三:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=l; i<=r; i++)
 5 typedef long long ll;
 6 using namespace std;
 7 
 8 const int N=21,mod=998244353;
 9 int n,m,x,y,inv[N],f[N],mx[1<<N],F[1<<N];
10 
11 int main(){
12     scanf("%d%d",&n,&m);
13     rep(i,1,m) scanf("%d%d",&x,&y),x--,y--,f[x]|=1<<y,f[y]|=1<<x;
14     inv[1]=1; f[0]|=1; F[0]=1;
15     rep(i,2,n) f[i-1]|=(1<<(i-1)),inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
16     for (int i=1; i<(1<<n); i++){
17         int tot=0;
18         for (int j=0; j<n; j++) if (i&(1<<j)){
19             int s=i&(~f[j]);
20             if (mx[i]<mx[s]+1) mx[i]=mx[s]+1,F[i]=0;
21             if (mx[i]==mx[s]+1) F[i]=(F[i]+F[s])%mod;
22             tot++;
23         }
24         F[i]=1ll*F[i]*inv[tot]%mod;
25     }
26     printf("%d\n",F[(1<<n)-1]);
27     return 0;
28 }

 

posted @ 2018-05-19 08:21  HocRiser  阅读(679)  评论(0编辑  收藏  举报