UVA 1390 DP+STL

UVA 1390 DP+STL

这道题目好好看过,但是没有好好想过, 网上的都是哈希,但是 看到一篇博客用vector和map写的是真好, 传送们

但是我现在还存在疑惑不懂的就是 30个点,最多会出现多少种分组情况, 这个最大值会不会爆出数组,但是我也没有从网上找到答案.所以对于这一题,我不确认这个算法能否再限定的时间下跑完最糟糕的情况 .

题意

输入一个n个点,m条边的无向图G( n ≤ 30 , m ≤ 1000 n\leq 30,m\leq 1000 n30m1000)。每次随机增加一条非自环的边(u,v)(加完后可以重边)。添加每条边的概率是相等的,求使G连通的期望操作次数。

分析

每个无向图看作一个状态,

当前状态先记作S, dp[S]表示这个状态的期望值,这个状态下有a个连通体,这些联通体的边数分别是 k i k_i ki

新连一条边,这条边重边的概率为
p 0 = ∑ i = 1 i = a C k i 2 C n 2 = ∑ i = 1 n k i ∗ ( k i − 1 ) n ( n − 1 ) p_0=\sum_{i=1}^{i=a}\frac{C_{k_i}^2}{C_n^2}=\frac{\sum_{i=1}^n k_i*(k_i-1)}{n(n-1)} p0=i=1i=aCn2Cki2=n(n1)i=1nki(ki1)

那么不重边的概率就是 1 − p 0 1-p_0 1p0

那么算出不重边的情况数,除以不重边的概率就可以知道当前这个状态下有多少种情况,故得总公式
d p [ S ] = ∑ i = 1 n ( d p [ s i ] ∗ p i ) + 1 1 − p 0 dp[S]=\frac{\sum_{i=1}^{n}(dp[s_i]*p_i)+1}{1-p_0} dp[S]=1p0i=1n(dp[si]pi)+1

代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1E6;
int n,m;
int g[40][40],vis[40],group[40];
double dp[MAXN];        //每种情况的期望值是double类型
vector<vector<int> >vs;  //每一种情况dp【s】都是一个vector<int>
map<vector<int>,int>ids;

void dfs(int a,int id)
{
    vis[a]=1;
    ++group[id];
    for(int i=1;i<=n;++i)
    {
        if(vis[i]==0&&g[a][i]==1)dfs(i,id);
    }
}
int ID(vector<int> v)
{
    if(ids.count(v)==0){
        ids[v]=vs.size();
        vs.push_back(v);
    }
    return ids[v];
}
inline  double  C(int x){ return  (double)x*(x-1)/2;  }
double  DP(int id)
{
    if(dp[id] )return dp[id];

    vector<int>v=vs[id];

    if(v.size()==1)return 0;
    double ci=0,cn=C(n);
    for(int i=0;i<v.size();++i)ci+=C(v[i]);
    double p=ci/cn,seg=0;

    for(int i=0;i<v.size();++i)
        for(int j=i+1;j<v.size();++j){
            double  pi=(v[i]*v[j])/cn;
            vector<int> tmp=v;
            tmp.erase(tmp.begin()+i);
            tmp.erase(tmp.begin()+j-1);
            tmp.push_back(v[i]+v[j]);
            sort(tmp.begin(),tmp.end());
            int id=ID(tmp);
            seg+=DP(id)*pi;
        }
    return dp[id]=(seg+1)/(1-p);
}
int main()
{
//    freopen("in.text","r",stdin);
    while(cin>>n>>m){
        memset(g,0, sizeof(g));
        memset(vis,0, sizeof(vis));
        memset(dp,0, sizeof(dp));
        memset(group,0, sizeof(group));
        int cnt=0;  //有几个连通体

        for(int a,b,i=1;i<=m;++i){cin>>a>>b;g[a][b]=g[b][a]=1;}
        for(int i=1;i<=n;++i)if(vis[i]==0)dfs(i,++cnt);

        sort(group+1,group+1+cnt);

        vector<int>tem(group+1,group+1+cnt);
        printf("%.6lf\n",DP(ID(tem)));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是Mally呀!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值