Codeforces 699e LRU

codeforces 699e LRU

题目链接

题意
给你n个视屏和一个大小为k的缓存区,每次选取一个视屏询问,如果视屏不在缓存区中,则加入缓存区,如果缓存区中视屏超过k个,则删除最早询问的视屏。给你每个视屏被询问的概率,每个视屏被选取的概率独立,问在10^100询问后每个视屏仍在缓存区内的概率。
思路
考虑到只有最后仍在缓存区中的视屏只有k个,所以对结果有影响的只有最后k次选取。
即前10^100 - k次选取的所有可能取法都对最后的结果没影响,所以在他们所有取法的基础上取就行了。
也就是说从后往前取,每当取到一个没有出现过的就加入集合,直到取满k个。
假如取到了重复的视屏,对集合的个数变化没有影响,所以可以看做是把这次取视屏与之前某次取不同视屏的操作交换,因为操作是独立的,所以不影响结果。
换言之,不需要考虑重复取的情况,直接算每次不同的转移即可。剩下的直接状压dp即可。
代码
#include <string.h>
#include <stdio.h>
double p[22],d[(1<<20) + 10];
double ans[22];
int hmany(int a){
    int ret = 0;
    while(a){
        if(a%2) ret ++;
        a >>= 1;
    }
    return ret;
}
int n,k;
int main(){
    scanf("%d%d",&n,&k);
    int num = 0;
    for(int i = 1;i <= n;i ++) {
        scanf("%lf",&p[i]);
        if(p[i] == 0) num ++;
    }
    if(n - num < k) k = n - num;
    memset(d,0,sizeof(d));
    d[0] = 1.0;
    for(int i = 0;i <(1<< n);i ++){
        int tmp = i;
        double tmpp = 0.0;
        int tmpk = 0;
        for(int j = 1;j <= n;j ++){
            if(i & (1 << (j - 1))){
                tmpk ++;
            }
            else tmpp += p[j];
        }
        if(tmpk >= k) continue;
        for(int j = 1;j <= n;j ++){
            if(((1 << (j - 1)) & i )== 0){
                d[i | (1 << (j - 1))] += d[i] * (p[j]/tmpp);
            }
        }
    }
    memset(ans,0,sizeof(ans));
    for(int i = 0;i < (1 << n);i ++){
        if(hmany(i) != k) continue;
        for(int j = 1;j <= n;j ++){
            if((1 << (j - 1)) & i){
                ans[j] += d[i];
            }
        }
    }
    for(int i = 1;i <= n; i ++){
        printf("%.10lf\n",ans[i] );
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值