[BZOJ 4117] Weather Report

Link:

BZOJ 4117 传送门

Solution:

第一次写$Huffman Tree$相关,发现就是个合并果子?

此题可以将每一种情况的概率和排列总数算出,接下来就是按照$Haffman Tree$基本构造方式操作了

 

注意,这里使用了分治的思想:

(1)如果排列总数大于1,先排除奇数影响,再将$P(pro,num)$变为$P(pro*2,num/2)$,

相当于将排列拆成相等的两部分再合并到一起

(2)如果排列总数等于1,再取下一个 概率最小的方式中的一个排列 合并即可

这样将排列方式不断减半、到一再合并的方法方便了概率的叠加

 

(Question:存疑点,为什么不能按照$pro*num$从小到大排序直接进行合并呢?)

Code:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<double,ll> P;
#define X first
#define Y second
const int MAXN=25;
int n;ll c[MAXN][MAXN];
double A,B,C,D,pa[MAXN],pb[MAXN],pc[MAXN],pd[MAXN],res;
priority_queue<P,vector<P>,greater<P> > q;

int main()
{
    c[0][0]=1;
    for(int i=1;i<=20;i++)
    {
        c[i][0]=1;
        for(int j=1;j<=20;j++) 
            c[i][j]=c[i-1][j]+c[i-1][j-1];
    }
    
    scanf("%d%lf%lf%lf%lf",&n,&A,&B,&C,&D);
    pa[0]=pb[0]=pc[0]=pd[0]=1.0; //预处理
    for(int i=1;i<=n;i++)
        pa[i]=pa[i-1]*A,pb[i]=pb[i-1]*B,pc[i]=pc[i-1]*C,pd[i]=pd[i-1]*D;
    for(int i=0;i<=n;i++) for(int j=0;j<=n;j++)
    for(int k=0;k<=n;k++) for(int l=0;l<=n;l++)
        if(i+j+k+l==n) q.push(P(pa[i]*pb[j]*pc[k]*pd[l],c[n][i]*c[n-i][j]*c[n-i-j][k]));
    
    while(true)
    {
        P cur=q.top();q.pop();
        if(q.empty() && cur.Y==1) break;
        if (cur.Y>1) //拆分+合并
        {
            if(cur.Y&1) q.push(P(cur.X,1)),cur.Y--;            
            res+=cur.X*cur.Y;
            q.push(P(cur.X*2,cur.Y>>1));
        }
        else //合并
        {
            P t=q.top();q.pop();
            res+=cur.X+t.X;
            q.push(P(cur.X+t.X,1));
            if(t.Y>1) q.push(P(t.X,t.Y-1));
        }
    }
    printf("%.6f",res);
    return 0;
}

 

posted @ 2018-06-28 21:56  NewErA  阅读(151)  评论(0编辑  收藏