IBM Ponder This November 2009【贪心】【哈夫曼树】

Description

Ponder This Challenge:
What is the minimal number, X, of yes/no questions needed to find the smallest (but more than 1*) divisor of a number between 2 and 166 (inclusive)?
We are asking for the exact answer in two cases:
In the worst case, i.e., what is the smallest number X for which we can guarantee finding it in no more than X questions?
On average, i.e., assuming that the number was chosen in uniform distribution from 2 to 166 and we want to minimize the expected number of questions.
* For example, the smallest divisor of 105 is 3, and of 103 is 103.
Update (11/05): You should find the exact divisor without knowing the number and answering “prime” is not a valid.

题解

第一问

​ 要最少的步数达到答案,肯定是采用二分的策略,直接按照二分的步骤模拟就可以。

第二问

​ 像第一问那样二分的过程肯定不能得到期望最小步数。考虑答案,其实就是

w[i]d[i]n
,其中, w[i] 表示最小质因数为第i中质因数的数的个数, d[i] 表示当心中想的数的最小质因子是第i中时,所需要的步数。 w[i] 其实就是叶子结点的权值, d[i] 其实就是合并的次数(到根节点的距离),这其实就是哈夫曼树,只要写个合并果子就可以了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define maxn 100006
#define nn 1e9
using namespace std;
inline char nc(){
    static char buf[100000],*i=buf,*j=buf;
    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline int _read(){
    int sum=0;char ch=nc();
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}
struct data{
    int x,p;
    bool operator <(const data&b)const{return p<b.p;}
}a[maxn];
int n,m,tot,ans,w[maxn],prime[maxn];
bool vis[maxn];
priority_queue< int,vector<int>,greater<int> > heap;
void make_prime(){
    memset(vis,1,sizeof(vis));
    for(int i=2;i<=sqrt(sqrt(nn));i++) if(vis[i])
     for(int j=2;j<=sqrt(nn)/i;j++) vis[i*j]=0;
    for(int i=2;i<=sqrt(nn);i++) if(vis[i])prime[++prime[0]]=i;
}
int main(){
    freopen("prime.in","r",stdin);
    freopen("prime.out","w",stdout);
    n=_read();
    make_prime();
    for(int i=1;i<=n;i++){
        a[i].x=_read();
        for(int j=1;j<=prime[0];j++) if(!(a[i].x%prime[j])){
            a[i].p=prime[j];break;
        }
        if(!a[i].p)a[i].p=a[i].x;
    }
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++){
        if(a[i].p!=a[i-1].p)m++;
        w[m]++;
    }
    int x=m;
    while(x>1){ans++;x=(x+1)/2;}
    printf("%d\n",ans);ans=0;
    for(int i=1;i<=m;i++)heap.push(w[i]);
    for(int i=1;i<m;i++){
        int x=heap.top();heap.pop();
        int y=heap.top();heap.pop();
        ans+=x+y;heap.push(x+y);
    }
    printf("%.6lf",(double)ans/n);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值