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!$就可以了

转移是枚举每个点,所以复杂度是$O(n3^n)$的($S2$一定是$S1$的子集)

后面的做法有点神仙

我不会做啊QAQ

观察题面,我们可以把点分为“考虑过的”和“现在还没考虑到的”

我们用$F_{(S,i)}$表示考虑了$S$集合,当前最大独立集为$i$的方案数其实可以优化,因为我们发现对于一个集合$S$,它的最大独立集是确定的,我们可以用$g[S]$来记录它,这是状态就可以优化一维

转移的时候,枚举一下第一个加到独立集里的点$i$,删去$i$和与$i$相邻的点,用它更新$g[S]$,再更新一下$F_S$即可

根本不可做呀

嘤嘤嘤

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == ‘-‘)f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - ‘0‘;
    return x * f;
}
const int maxn = (1 << 20) + 10,mod = 998244353;
int n,m;
int a[21];
int dp[maxn],mx[maxn];
int fac[30],ifac[30];
inline int ksm(int x,int t)
{
    int res = 1;
    while(t)
    {
        if(t & 1)res = res * x % mod;
        x = x * x % mod;
        t = t >> 1;
    }
    return res;
}
inline int A(int n,int m)
{
    return fac[n] * ifac[n - m] % mod;
}
signed main()
{
    n = read(),m = read();
    for(int i=1;i<=m;i++)
    {
        int u = read() - 1,v = read() - 1;
        a[u] |= (1 << (v));
        a[v] |= (1 << (u));
    }int MAXSTATE = (1 << n) - 1;
    fac[0] = 1,ifac[0] = 1;ifac[1] = 1;
    for(int i=1;i<=29;i++)fac[i] = (fac[i - 1] * i) % mod,ifac[i] = ksm(fac[i],mod - 2);
    //for(int i=1;i<=20;i++)cout<<fac[i]<<" "<<ifac[i]<<endl;

    //for(int i=0;i<=n;i++)a[i] |= i;
    dp[0] = 1;
    for(int s=0;s<=MAXSTATE;s++)
        if(dp[s])
        {
            for(int i=0;i<n;i++)
                if (!(s>>i&1))
                {
                    int t = s | a[i] | (1 << i);
                    if(mx[t] < mx[s] + 1)mx[t] = mx[s] + 1, dp[t] = 0;
                    if(mx[t] == mx[s] + 1)
                        dp[t] = (dp[t] + dp[s] * A(n - __builtin_popcount(s) - 1,__builtin_popcount(a[i]) - __builtin_popcount(a[i] & s))) % mod;
                }
        }
    cout<<dp[MAXSTATE] * ifac[n] % mod;
}

原文地址:https://www.cnblogs.com/Kong-Ruo/p/9836792.html

时间: 2024-11-06 14:03:01

PKUWC2018 随机算法的相关文章

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

题目分析: 听说这题考场上能被$ O(4^n) $的暴力水过,难不成出题人是毕姥爷? 首先思考一个显而易见的$ O(n^2*2^n) $的暴力DP.一般的DP都是考虑最近的加入了哪个点,然后删除后递归进行状压DP.由于这道题的题目询问方式是反过来的,处理方式也反过来. 令$ f[n][S] $表示当前有$ S $这些点,期望这些点能够构成独立集大小为$ n $.正向的考虑选择了哪个点,并把与这个点有连边的所有点在集合内进行删除,令找到的新状态为$ f[n-1][P] $.我们把$ P $中的结点

[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时能得到的最大独立集大小,考虑转移,枚举排列里最后一个在独立

Miller_Rabin算法(随机算法,判断一个数是否是素数)

1 const int S = 20;//随机算法判定次数,S越大,判错概率越小 2 LL pow_mod(LL a, LL b, LL mod) { // a^b%mod 3 LL ans = 1; 4 a = a % mod; 5 while(b) { 6 if(b & 1) { 7 ans = (ans * a) % mod; 8 } 9 a = ( a * a ) % mod; 10 b >>= 1; 11 } 12 return ans; 13 } 14 bool check

POJ 3318:Matrix Multiplication(随机算法)

http://poj.org/problem?id=3318 题意:问A和B两个矩阵相乘能否等于C. 思路:题目明确说出(n^3)的算法不能过,但是通过各种常数优化还是能过的. 这里的随机算法指的是随机枚举矩阵C的一个位置,然后通过A*B计算是否能够得到矩阵C相应位置的数,如果不等,就直接退出了,如果跑过一定的数量后能够相等,那么就可以判断这个矩阵C等于A*B的.第一次见这样的题目...有点新奇. 暴力算法: 1 #include <cstdio> 2 using namespace std;

微信红包随机算法

最近看了一篇文章,讲微信红包随机算法的.感觉很不错,所以自己实现了下,并进行了简单测试. 算法 算法很简单,不是提前算好,而是抢红包时计算: 红包里的金额怎么算?为什么出现各个红包金额相差很大?答:随机,额度在0.01和剩余平均值*2之间. 实现 实现上述算法的逻辑主要是: public static double getRandomMoney(RedPackage _redPackage) { // remainSize 剩余的红包数量 // remainMoney 剩余的钱 if (_red

加权随机算法

加权随机算法一般应用在以下场景:有一个集合S,里面比如有A,B,C,D这四项.这时我们想随机从中抽取一项,但是抽取的概率不同,比如我们希望抽到A的概率是50%,抽到B和C的概率是20%,D的概率是10%.一般来说,我们可以给各项附一个权重,抽取的概率正比于这个权重.那么上述集合就成了: {A:5,B:2,C:2,D:1} 方法一: 扩展这个集合,使每一项出现的次数与其权重正相关.在上述例子这个集合扩展成:{A,A,A,A,A,B,B,C,C,D}然后就可以用均匀随机算法来从中选取. 好处:选取的

权重随机算法的java实现

一.概述 平时,经常会遇到权重随机算法,从不同权重的N个元素中随机选择一个,并使得总体选择结果是按照权重分布的.如广告投放.负载均衡等. 如有4个元素A.B.C.D,权重分别为1.2.3.4,随机结果中A:B:C:D的比例要为1:2:3:4. 总体思路:累加每个元素的权重A(1)-B(3)-C(6)-D(10),则4个元素的的权重管辖区间分别为[0,1).[1,3).[3,6).[6,10).然后随机出一个[0,10)之间的随机数.落在哪个区间,则该区间之后的元素即为按权重命中的元素. 实现方法

随机算法 - HNU 13348 Finding Lines

Finding Lines Problem's Link: http://acm.hnu.cn/online/?action=problem&type=show&id=13348&courseid=0 Mean: 给你平面上1e5个点,让你判断是否可以找到一条直线,使得p%的点都在这条直线上. analyse: 经典的随机算法题. 每次随机出两个点,然后对n个点进行判断,看是否有p%的点在这条直线上. 关于随机算法正确性的证明: 每次随机一个点,这个点在直线上的概率是p,p最小为2

由抽奖软件想到的随机算法总结

整整三年没更新博客了,今天和女友聊天,聊到了博客,就回来看看. 最近接触到抽奖软件,下载的源码是http://download.csdn.net/detail/ghz_sd/6918125,在这里为开源软件作出贡献的人致敬,这个软件的作者a米山,是个非常好的人,耐心的帮我调试,他的算法很简单,就是纯粹的random,用的rand()函数,我给他提了个需求,写一个作弊类,实现的功能是:指定人的中奖概率提高,配置文件类似于这样: <xml> <win> <name='a' pro