第23次CSP认证 第4题 收集卡牌(记忆化搜索,状压)

链接:http://118.190.20.162/view.page?gpid=T132
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
思路:先写一个暴力搜索,然后把中间结点的状态存起来,就是记忆化搜索,状态要存搜索到的层数和选择的卡牌数,0代表不选,1代表选,可以把16个卡牌是否选的状态压缩到2的16次方的一个int数中。
注意:1.题目有一个坑点,题目里的精度是骗人的,输出精度要达到1e-10才行,1e-5的精度会WA
2.dp数组第一维度保存层数,极端情况是是16张牌,一张牌概率0.9985,其他牌概率0.0001,k最大=5,所以树的深度最深是75左右,所以开100就够,存01状压的维度,要大于2的16次方也就是65536,所以可以开70000
3.考场上脑抽了,更新了dp数组后,没有用dp数组已经保存的值,而是又dfs下去了。。然后超时
先上20分的暴力搜索代码

2022/5/9更新:
好多人私信我问我代码的问题,我先讲一下暴力递归的分析思路:
先是想一颗树,第一层是N张牌,对于第一层的每张牌,第二层再抽N张牌,以此类推,对这颗树进行搜索的时候,就是进行一次深搜,我最终的期望相当于第一层每个结点期望的加权求和,那就是抽到每张牌的概率p乘上这个结点的期望值。之后的层同理。但是发现搜索空间太大,必须记忆化,这是dp类题目。
做dp类题目先想状态的表示,这个题根据数据可以知道要用状态压缩dp,即0和1代表卡牌的有无,但是只用一维不能解决问题,当时我是先写出了暴力的代码,根据函数的参数发现层数也需要存一下,相当于一共参与了几次抽卡。
然后发现状态转移和暴力的一样,于是改动不大,只需要把状态存起来就好。

#include <bits/stdc++.h>
const int maxn=50;
using namespace std;
double p[maxn+5];
int book[maxn+5];
int N,K;
double dfs(int now,int lev,int left){
    if((lev-(N-left))/K>=left)return lev;
    int flag=1;
    for(int i=0;i<N;i++){
        if(book[i]==0)flag=0;
    }
    if(flag==1)return lev;
    double ans=0;
    for(int i=0;i<N;i++){
        if(book[i]==0){
            book[i]=1;
            ans+=p[i]*dfs(i,lev+1,left-1);
            book[i]=0;
        }
        else ans+=p[i]*dfs(i,lev+1,left);
    }
    return ans;
}
int main()
{
    cin>>N>>K;
    for(int i=0;i<N;i++){
        cin>>p[i];
    }
    double ans=0;
    for(int i=0;i<N;i++){
        book[i]=1;
        ans+=p[i]*dfs(i,1,N-1);
        book[i]=0;
    }
    cout<<ans<<endl;
    return 0;
}

然后是满分的状压+记忆化搜索代码

#include <bits/stdc++.h>
const int maxn=50;
using namespace std;
double p[maxn+5];
int book[maxn+5];
int N,K;
double dp[1000][70000];
double dfs(int lev,int left,int state){//层数,剩余牌数,01选或者不选的状态
    if((lev-(N-left))/K>=left)return lev;
    double ans=0;
    for(int i=0;i<N;i++){
        int temps;
        if(book[i]==0){
            book[i]=1;
            temps=state|(1<<i);
            if(dp[lev+1][temps]==0){
                dp[lev+1][temps]=dfs(lev+1,left-1,temps);
            }
            ans+=p[i]*dp[lev+1][temps];
            book[i]=0;
        }
        else{
            if(dp[lev+1][state]==0){
                dp[lev+1][state]=dfs(lev+1,left,state);
            }
            ans+=p[i]*dp[lev+1][state];
        }
    }
    return ans;
}
int main()
{
    cin>>N>>K;
    for(int i=0;i<N;i++){
        cin>>p[i];
    }
    double ans=0;
    for(int i=0;i<N;i++){
        book[i]=1;
        int st=(1<<i);
        if(dp[1][st]==0){
            dp[1][st]=dfs(1,N-1,st);
        }
        ans+=p[i]*dfs(1,N-1,st);
        book[i]=0;
    }
    printf("%.10f",ans);
    return 0;
}

考完以后写了一个更短的。。

#include <iostream>
const int maxn=30;
using namespace std;
int N,K;
double p[maxn+5];
int book[maxn+5];
double dp[100][80000];
double dfs(int level,int have,int state){//层数,已经抽中的卡牌数,01状态
    if((N-have)*K<=level-have){
        return level;
    }
    double ans=0;
    for(int i=0;i<N;i++){
        if(book[i]==0){
            book[i]=1;
            if(dp[level+1][state+(1<<i)]==0){
                dp[level+1][state+(1<<i)]=dfs(level+1,have+1,state+(1<<i));
            }
            ans+=p[i]*dp[level+1][state+(1<<i)];
            book[i]=0;
        }
        else{
            if(dp[level+1][state]==0){
                dp[level+1][state]=dfs(level+1,have,state);
            }
            ans+=p[i]*dp[level+1][state];
        }
    }
    return ans;
}
int main()
{
    cin>>N>>K;
    for(int i=0;i<N;i++){
        cin>>p[i];
    }
    double ans=dfs(0,0,0);
    printf("%.10f",ans);
    return 0;
}

  • 11
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问,欢迎随时与博主沟通,博主会及时解答。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值