• 概率期望训练之三


    http://acm.hdu.edu.cn/showproblem.php?pid=5036

    题目大意:

    开始你没有任何钥匙,而你面前有很多道门,每道门后面可能有打开其他门的钥匙,如果你无法打开这道门,可以选择将它炸开,求打开所有门的期望炸的次数

    分析:

    首先根据期望的线性性

    炸开所有门的期望次数=Σ每道门的期望次数

    但这道题烦就烦在有可能会有环

    先建图

    假如没有环的话,ans=Σ1/dep[i]

    但现在有环怎么办?

    你肯定和我想的一样,Tarjan缩点呗

    然后就莫名其妙的wa了(应该是我写错了,正解只是更简化了)

    wa code:(不想看就跳过)

    #include<bits/stdc++.h>
    #define ll long long
    #define il inline
    #define ri register int
    #define lowbit(x) x&(-x)
    using namespace std;
    const int maxn=1e3+5;
    bool vis[maxn];
    queue<int>S;
    vector<int>Q[maxn],t[maxn];
    double ans;
    int T,cnt,top,sum,n,ca;
    int dfn[maxn],deg[maxn],sz[maxn],belong[maxn],low[maxn],stk[maxn],dep[maxn];
    il void Tarjan(int u){
    	dfn[u]=low[u]=++cnt;vis[u]=true;
    	stk[++top]=u;
    	for(ri i=0;i<Q[u].size();i++){
    		int to=Q[u][i];
    		if(!dfn[to])Tarjan(to),low[u]=min(low[to],low[u]);
    		else if(vis[u])low[u]=min(dfn[to],low[u]);
    	}
    	if(dfn[u]==low[u]){
    		++sum;sz[sum]=0;t[sum].clear();int tp;
    		do{
    			tp=stk[top--];
    			vis[tp]=false;
    			belong[tp]=sum;
    			sz[sum]++;
    		}while(tp!=u);
    }}
    int main(){
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d",&n);
    		for(ri i=1;i<=n;i++){deg[i]=dfn[i]=low[i]=dep[i]=0;
    			int tot;scanf("%d",&tot);
    			Q[i].clear();
    			for(ri j=1;j<=tot;j++){
    			int x;scanf("%d",&x);
    			if(x==i)continue;
    			Q[i].push_back(x);
    			}
    		}
    		cnt=top=sum=ans=0;
    		for(ri i=1;i<=n;i++)if(!dfn[i])Tarjan(i);
    		for(ri u=1;u<=n;u++)
    			for(ri i=0;i<Q[u].size();i++){
    				int to=Q[u][i];
    				if(belong[to]!=belong[u])t[belong[u]].push_back(belong[to]),deg[belong[to]]++;
    			}
    			for(ri i=1;i<=sum;i++)if(!deg[i]){S.push(i),dep[i]=sz[i];}
    			while(!S.empty()){
    				int tmp=S.front();S.pop();
    				ans+=(double)sz[tmp]/dep[tmp];
    			for(ri i=0;i<Q[tmp].size();i++){
    				int to=Q[tmp][i];
    				deg[to]--;dep[to]+=dep[tmp];
    			    if(!deg[to]){
    			    	dep[to]+=sz[to];
    			    	S.push(to);
    				}
    			}	
    			}
    		printf("Case #%d: %.05f
    ", ++ca, ans);
    	}
    	return 0;
    }
    /*
    2
    3
    1 2
    1 3
    1 1
    3
    0
    0
    0
    */
    
    

    正解

    考虑传递闭包:

    所谓传递闭包就是A能够到B,B能够到C,那么A也就能够到C

    考虑集合S能够到A,集合T能够到B,A能够到B,则A|S|T就能够到B

    bitset维护即可

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <bitset>
    #include <algorithm>
    using namespace std;
    const int maxn = 1e3 + 5;
    bitset<maxn> v[maxn];
    int main()
    {
        int t, ca = 1, n, k, x;
        cin >> t;
        while(t--)
        {
            scanf("%d", &n);
            for (int i = 0; i <= n; i++)
                v[i].reset(),v[i][i] = true;//它自己也要算上 
            for(int i = 1; i <= n; i++)
            {
                scanf("%d", &k);
                for(int j = 1; j <= k; j++)
                    scanf("%d", &x),v[i][x] = 1;
            }
            for(int i = 1; i <= n; i++)  
                for(int j = 1; j <= n; j++)
                    if(v[j][i])
                        v[j] |= v[i];//传递闭包 
            double ans = 0;
            for(int i = 1; i <= n; i++)
            {
                int cnt = 0;
                for(int j = 1; j <= n; j++)
                    if(v[j][i]) cnt++;
                ans += 1.0 / cnt;
            }
            printf("Case #%d: %.05f
    ", ca++, ans);
        }
        return 0;
    }
    
  • 相关阅读:
    SQLSERVER2000使用TSQL将数据导入ACCESS并压缩生成rar
    收回动态VHD的未使用空间
    如何在html中添加引用公共模块文件 bling
    C语言I博客作业02
    C语言I博客作业06
    C语言I博客作业05
    C语言I博客作业04
    C语言I博客作业03
    Kali 安装JAVA(来源:oschina)
    DHCP服务器内网攻击测试
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/11748999.html
Copyright © 2020-2023  润新知