Topcoder SRM 562 DIV2 900 RandomOption


Task
给定 N(5<=N<=14) 个数分别为 0,1,,n1 ,对这 N 个数进行排列,同时有M(1<=M<=50)组限制,表示 (u,v) 两个数不能放在相邻的位置上。现在对这个数列进行随机排列,求满足题意要求的概率。


Solution
几乎是一道裸的状压dp题(虽然我没有看出来)。我们定义 dp[Set][pre] 表示当前还没有选的元素集合为 Set ,上一次挑选的元素为 pre
首先考虑递归的做法:
对于当前层 Set ,我们可以从 Set 中挑选1个元素作为下一层的 pre 。那么一开始我们可以调用 f(Alllanes,n) 表示当前所有的元素都可以调用,并且还未选任何元素,因为我们已经要求所有元素的下标范围为 [0,n1]

在递归调用中就只会出现两种情况:
1.最基本的情况, Set= ,这意味着所有元素已经被取光。这种情况唯一的排列就是空排列,并且没有被ban掉的情况,那么概率就是1.0;
2.中间情况,在 Set 中有 cnt 个元素,每个元素被挑选的概率为 1cnt ,那么每个子状态的概率加权的值就是当前层的概率了。
再考虑到有ban的要求,那么如果 (pre,x) 是一组被ban的数对,那么这个子状态的可能性就为0.0;

根据以上分析,就可以轻松写出这个裸状压dp:

class RandomOption {
public:
    int Ban[15][15];
    int All_lane,n;
    double dp[1<<14][15];
    double rec(int Set_lane,int pre){
        double &res=dp[Set_lane][pre];
        if(res)return res;
        int cnt=0;
        for(int x=0;x<n;x++)
            if(Set_lane>>x&1){
                if(!Ban[pre][x])
                    res+=rec(Set_lane-(1<<x),x);
                cnt++;
            }
        if(!cnt)res=1.0;
        else res/=1.0*cnt;
        return res;
    }
    double getProbability(int keyCount,vector<int> bad1,vector<int> bad2) {
        for(int i=0;i<bad1.size();i++){
            int u=bad1[i],v=bad2[i];
            Ban[u][v]=Ban[v][u]=1;
        }
        n=keyCount,All_lane=(1<<n)-1;//0 ~ 14
        return rec(All_lane,n);
    }
};

接下来根据递归的思路写出递推:

由于每一层的状态都是 pair(Set,pre) Set 始终是含有 keyCount 个元素的全集的子集,所以枚举状态的复杂度是 O(2keyCountkeyCount) 。接下来枚举要转移的元素To,复杂度是 O(keyCount) ,所以总复杂度是喜闻乐见的 O(2nn2) 。要注意的是本题中 keyCount 的范围只有14,所以这样考虑正合适。

class RandomOption {
public:
    int Ban[15][15];
    int All_lane,n;
    double dp[1<<14][15];
    double getProbability(int keyCount,vector<int> bad1,vector<int> bad2) {
        for(int i=0;i<bad1.size();i++){
            int u=bad1[i],v=bad2[i];
            Ban[u][v]=Ban[v][u]=1;
        }
        n=keyCount,All_lane=(1<<n)-1;
        for(int i=0;i<n;i++)dp[0][i]=1.0;//特判Set为空集的情况 
        for(int S=1;S<All_lane;S++){
            int cnt=0;
            for(int i=0;i<n;i++)if(S>>i&1)cnt++;
            for(int pre=0;pre<n;pre++){
                if(S>>pre&1)continue;
                for(int to=0;to<n;to++){
                    if((S>>to&1)&&!Ban[pre][to])
                        dp[S][pre]+=dp[S-(1<<to)][to]/(1.0*cnt);
                }
            }
        }
        for(int to=0;to<n;to++)//特判Set为全集的情况 
            dp[All_line][n]+=dp[All_line-(1<<to)][to]/(1.0*keyCount);
        return dp[All_lane][n];
    }
};
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值