Topcoder SRM 562 DIV2 900 RandomOption

来源:互联网 发布:股中王股票行情软件 编辑:程序博客网 时间:2024/06/06 00:17

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];    }};
0 0
原创粉丝点击