CSP第23次 202109-4 收集卡牌 C语言答案

CSP第23次 202109-4 收集卡牌 C语言20分答案

注意:
1、如果你是想多次*=的话,使用pow代替计算可以增加结果精确度
2、浮点数计算会有误差要注意判断条件

只拿了20分,想了两种递归的思路都是运行超时,不过应该可以为没有头绪的读者提供一点灵感,就先放出来了,日后再更新。

现在的情况是还需要优化效率,有一点头绪:分治法,把原问题分解成每层递归时可以计算的子问题进行计算,然后把计算结果记录下来,每层根据上一层的计算结果来推算。(用vector+动态规划尝试了下,还是超时,暂时放弃这题了)
在这里插入图片描述

//纯C,第一种思路:递归返回底层结果 逐层累积为最终结果(由底层到表层),20分,运行超时
#include <stdio.h>

const double eps=1e-8;//浮点数运算误差值
int n,k;
double maby[16]={-1};
int haveCardCount[16]={0};//用于记录当前每种卡的抽到过次数

//深度优先搜索算法
double DFS(int coin,int count)//coin当前持有硬币数,count当前是第几次抽卡
{
    int i,j,needCard=0;
    double ans=0;
    for(j=0;j<n;j++)
        if(haveCardCount[j]==0)
            needCard++;
    if(coin/k+eps>=needCard)//完成一种游戏结果,系统栈出栈
    {
        ans = count;
    }else
         for(i=0;i<n;i++)//当前这一次抽卡的每种情况都需要枚举
        {
            if(haveCardCount[i]>0)
            {
                haveCardCount[i]++;
                ans+=DFS(coin+1,count+1)*maby[i];
            }
            else
            {
                haveCardCount[i]++;
                ans+=DFS(coin,count+1)*maby[i];
            }
            haveCardCount[i]--;
        }
    return ans;
}

int main()
{
    int i;
    scanf("%d %d",&n,&k);
    for(i=0;i<n;i++)
        scanf("%lf",&maby[i]);
    printf("%.10f",DFS(0,0));
    return 0;
}

//纯C,第二种思路:把计算结果设为全局变量 每次递归时累积(由表层到底层),10分,运行超时
#include <stdio.h>
#include <math.h>

const double eps=1e-8;//浮点数运算误差值
int n,k,needCard;//needCard还差几种卡
double maby[16]={-1};
int haveCardCount[16]={0};//用于记录当前每种卡的抽到过次数
double out=0;

//深度优先搜索算法
void DFS(int coin,int count)//coin当前持有硬币数,count当前是第几次抽卡
{
    int i,j;
    count++;
    for(i=0;i<n;i++)//当前这一次抽卡的每种情况都需要枚举
    {
        int _coin=coin;
        if(haveCardCount[i]>0)_coin++;
        else needCard--;
        haveCardCount[i]++;
        if(_coin/k+eps>=needCard)//完成一种游戏结果,系统栈出栈
        {
            double tmp =1;
            for(j=0;j<n;j++)
                tmp*=pow(maby[j],haveCardCount[j]);//小细节pow一次性计算可以增加结果精确度
            out+=tmp*count;
        }else//递归下一次抽卡,系统栈压栈
        {
            DFS(_coin,count);
        }
        haveCardCount[i]--;
        if(!haveCardCount[i])needCard++;
    }
}

int main()
{
    int i;
    scanf("%d %d",&n,&k);
    needCard=n;
    for(i=0;i<n;i++)
        scanf("%lf",&maby[i]);
    DFS(0,0);
    printf("%.10f",out);
    return 0;
}

记录一下,下方是我的DFS第一版本代码,运行超时+准确度不足
(题目给的两个案例都准确完成,但是跟满分代码相比较后发现数据量大时准确度就不足了)
在这里插入图片描述

//10分代码,错误+运行超时
#include <stdio.h>
#include <vector>
using namespace std;

double maby[16]={-1};
int haveCardCount[16]={0};
double out=0;
int n,k;
vector<int> v;

void DFS(int coin,int count)
{
    int i,j,jj;
    count++;
    for(i=0;i<n;i++)
    {
        int _coin=coin;
        haveCardCount[i]++;
        for(vector<int>::iterator it=v.begin();it!=v.end();it++)
        {
            if(*it==i)
            {
                _coin++;
                break;
            }
        }
        v.push_back(i);
        int needCard=0;
        for(j=0;j<n;j++)
        {
            if(haveCardCount[j]==0)
            needCard++;
        }
        if(1.0*_coin/k>=needCard)
        {
            double tmp =1;
            for(j=0;j<n;j++)
            {
                for(jj=0;jj<haveCardCount[j];jj++)
                    tmp*=maby[j];
            }
            out+=tmp*count;
        }else
        {
            DFS(_coin,count);
        }
        haveCardCount[*(v.end()-1)]--;
        v.pop_back();
    }
}

int main()
{
    int i;
    scanf("%d %d",&n,&k);
    for(i=0;i<n;i++)
    {
        scanf("%lf",&maby[i]);
    }
    DFS(0,0);
    printf("%.10f",out);
    return 0;
}

记录一下,下方是我的DFS第二版本代码,变成仅运行超时?!
第一版和第二版仅修改了判断条件,eps为“浮点数误差值”

第一版的判断条件if(1.0*_coin/k>=needCard)
第二版的判断条件if(1.0*_coin/k+eps>=needCard)

发现第一版错误是因为浮点数的比较问题!!!兄弟们以后写代码可记住这个问题了,别坏了大事
计算机中的浮点数存储不是绝对精准的,double类型可以存储约小数点后15~16位,当经过多次运算之后会出现很小很小的值误差变化,根据教科书上的说明,这个误差变化一般不会大于下面这个值

const double eps=1e-8;//科学记数法表示,10的-8次方

在这里插入图片描述

//10分代码,运行超时
#include <stdio.h>
#include <vector>
using namespace std;

const double eps=1e-8;
double maby[16]={-1};
int haveCardCount[16]={0};
double out=0;
int n,k;
vector<int> v;

void DFS(int coin,int count)
{
    int i,j,jj;
    count++;
    for(i=0;i<n;i++)
    {
        int _coin=coin;
        haveCardCount[i]++;
        for(vector<int>::iterator it=v.begin();it!=v.end();it++)
        {
            if(*it==i)
            {
                _coin++;
                break;
            }
        }
        v.push_back(i);
        int needCard=0;
        for(j=0;j<n;j++)
        {
            if(haveCardCount[j]==0)
            needCard++;
        }
        if(1.0*_coin/k+eps>=needCard)
        {
            double tmp =1;
            for(j=0;j<n;j++)
            {
                for(jj=0;jj<haveCardCount[j];jj++)
                    tmp*=maby[j];
            }
            out+=tmp*count;
        }else
        {
            DFS(_coin,count);
        }
        haveCardCount[*(v.end()-1)]--;
        v.pop_back();
    }
}

int main()
{
    int i;
    scanf("%d %d",&n,&k);
    for(i=0;i<n;i++)
    {
        scanf("%lf",&maby[i]);
    }
    DFS(0,0);
    printf("%.10f",out);
    return 0;
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琴kk

给我一点点鼓励吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值