集合划分状压dp

 

给一个 n 个点 m 条边的无向图,每条边有 pi 的概率消失,求图连通的概率

n9

sol:

我们考虑一个 dp 

f(i,S) 表示只考虑前 i 条边,当前图连通的状态为 S 的概率

设这条边没有消失,图的新连通状态为 T

那转移到 T 的概率就是 (1pi)

不变的概率是 pi

然后一个滚动数组就做完了

 

然后我们考虑,怎么把“图的连通状态”这个东西状压出来

一个 idea 是,我们可以在状态里记录每个点所处的连通块里最小的点的编号,比如 123 是一个连通块,45 是一个连通块,我们这个状态就是 12344 

这样的话,因为每个点所处连通块里最小的点的编号不超过它的编号,状态数是 O(n!) 的,但如果我们直接存一个 9 位数,数组显然开不下

1.我们可以哈希一下,把代码写成这样

复制代码
int h;
map<vector<int>,int> hsh;
map<int,vector<int> > reh;
inline int gethsh(vector<int> v)
{
    if(!hsh[v])
    {
        hsh[v] = ++h;
        reh[h] = v;
    }
    return hsh[v];
}
inline vector<int> getreh(int h){return reh[h];}
复制代码

2.或者我们可以动动脑子

显然,这个状态的最后一位数只有 n 种情况

然后,倒数第二位只有 n1 种情况(不能比最后一位大)

然后,倒数第三位只有 n2 种情况

...

然后,最高位只有 1 种情况(1 只能属于 1

于是我们把最后一位数乘以 n ,倒数第二位乘以 (n1)×n ,倒数第三位乘以 (n2)×(n1)×n ... 最高位乘以 n!

就把状态压到了 O(n!) 个数里

如果想知道一个值对应的状态是什么,从高到低除再取余一下就可以了

于是可以 dp 了

 

学习了 yyc 同学的写法,预处理出了前 n 位所有状态

state(i,j) 表示第 i 个方案的第 j 位是什么,也就是 j 点在第 i 种状态里所属的连通块的编号最小的点

frustrated好强呀

%%%

复制代码
#include<bits/stdc++.h>
#define LL 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;
}
int n,m;
double grid[15][15];
namespace p30
{
    int fa[15];inline int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
    struct EDG{int u,v;double p;}es[100];
    int _main()
    {
        for(int i=1;i<=m;i++)
        {
            int a = read(),b = read();double p;
            scanf("%lf",&p);
            //grid[a][b] = grid[b][a] = (1.00 - p);
            es[i] = (EDG){a,b,1.00 - p};
        }
        int MAXSTATE = (1 << m) - 1;
        double zgl = 0.0;
        for(int S=0;S<=MAXSTATE;S++)
        {
            double curgl = 1.00;
            for(int i=1;i<=n;i++)fa[i] = i;
            for(int i=1;i<=m;i++)
            {
                if(S & (1 << (i - 1)))
                {
                    int fu = find(es[i].u),fv = find(es[i].v);
                    curgl *= es[i].p;if(fu == fv)continue;
                    fa[fu] = fv;
                }
                else curgl *= (1.00 - es[i].p);
            }
            int flg = 1;
            for(int i=1;i<=n;i++)if(find(i) != find(1))flg = 0;
            if(flg)zgl += curgl;
        }printf("%.3f\n",zgl);
        return 0;
    }
}
namespace prng
{
    int _main()
    {
        srand(time(0));
        int RNG = rand() % 1000 + 1;
        double rng = RNG / 1000.0;
        printf("%.3f\n",rng);
        return 0;
    }
}
namespace p100
{
    const int maxn = 382880;
    int fac[15],u[150],v[150],state[maxn][12],t[12];
    double w[150],f[maxn],g[maxn];
    inline int calc()
    {
        int res = 0;
        for(int i=1;i<=n;i++) res += (t[i] - 1) * (fac[n] / fac[i]);
        return res + 1;
    }
    int _main()
    {
        for(int i=1;i<=m;i++)
        {
            u[i] = read(),v[i] = read();
            scanf("%lf",&w[i]);
        }fac[0] = 1;
        for(int i=1;i<=10;i++)fac[i] = fac[i - 1] * i;
        for(int i=1;i<=n;i++)state[1][i] = 1;
        for(int i=2;i<=fac[n];i++)
        {
            int x = n;
            while(1)
            {
                state[i][x] = state[i - 1][x] + 1;
                if(state[i][x] > x)state[i][x] = 1,state[i][x - 1]++;
                else break;
                x--;
            }
            for(int j=1;j<x;j++)state[i][j] = state[i - 1][j];
        }
        f[fac[n]] = 1.0;
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=fac[n];j++)
            {
                if(f[j])
                {
                    for(int kk=1;kk<=n;kk++)t[kk] = state[j][kk];
                    for(int kk=1;kk<=n;kk++)
                        if(state[j][v[i]] == t[kk] || t[kk] == state[j][u[i]]) t[kk] = min(state[j][u[i]],state[j][v[i]]);
                    g[calc()] += f[j] * (1 - w[i]);
                    g[j] += f[j] * w[i];
                }
            }
            for(int j=1;j<=fac[n];j++)f[j] = g[j],g[j] = 0;
        }
        printf("%.3f",f[1]);
    }
}
int main()
{
    //freopen("10.in","r",stdin);
    //freopen("10.out","w",stdout);
    n = read(),m = read();
    //if(n <= 8 || m <= 23)p30::_main();
    //else prng::_main();
    p100::_main();
}
View Code
复制代码

 

posted @   探险家Mr.H  阅读(286)  评论(0编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· 程序员常用高效实用工具推荐,办公效率提升利器!
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 【译】WinForms:分析一下(我用 Visual Basic 写的)
点击右上角即可分享
微信分享提示