第十二届蓝桥杯b组题解

砝码称重

在这里插入图片描述

代码

#include<iostream>

using namespace std;
//总重量的数量级是1e5,为了保险起见开了1e6的
const int N = 105,M=1e6+10;

bool f[N][M];//这里储存每个的状态,第一个表示砝码的数量,第二个表示重量

int a[N];//每个砝码的重量

int n,sum;
int main()
{
    cin>>n;
    
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        sum+=a[i];//计算砝码重量之和
    }
    
    f[0][0]=true;//当输入0个重量为0
    
    for(int i=1;i<=n;i++)
    {
        for(int  j=0;j<=sum;j++)
        {
   //分别表示了三种可能
   //1.当前不动砝码
   //2.重量加砝码,表示j+a[i]
   //3.重量减去当前砝码,表示为j-a[i]
   //当有一个是可行的那么就是可行的
            f[i][j]=f[i-1][j]||f[i-1][j+a[i]]||f[i-1][abs(j-a[i])];
        }
    }
    
    int ans=0;
    
    for(int j=1;j<=sum;j++)
        {if(f[n][j])ans++;}
		//最后在n的情况下,有哪些重量是可得到的
    cout<<ans<<endl;
}

疑问:
按照这个思想在录入时,将单个砝码的重量都表示为可以

 for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        sum+=a[i];//计算砝码重量之和
 		f[1][a[i]]=true;
    }

然后在下面循环中,放置两个砝码开始

for(int i=2;i<=n;i++)
    {
        for(int  j=0;j<=sum;j++)
        {
 
            f[i][j]=f[i-1][j]||f[i-1][j+a[i]]||f[i-1][abs(j-a[i])];
        }
    }

按逻辑来想,应该是对的,但是连样例也过不了

总结:

知识点:dp,递推

这里类似01背包中,但是砝码的放置存在放左边和放右边的不同,所以比01背包多一个选项。
在做此类问题时,还是跟着y总的dp大法

1.状态表示
2.状态计算(状态的转移)

杨辉三角

在这里插入图片描述

代码

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

typedef long long ll;

int n;


//求组合数
ll c(int a,int b)
{
    ll res=1;
    for(int i=a,j=1;j<=b;j++,i--)
        {
            res=res*i/j;
            if(res>n)return res;
        }
    return res;
}

//二分查找里面最大的
bool check(int k)
{
    int l=2*k,r=max(l,n);
    while(l<r)
    {
        int mid=l+r>>1;//找出l与r一半的数
        if(c(mid,k)>=n)
        {
            r=mid;
        }
        else{
            l=mid+1;
        }
    }
    if(c(r,k)!=n)return false;
    //1ll是为了防止爆出int的数据范围
    cout<<1ll*(r+1)*r/2+k+1<<endl;
    return true;
}

int main()
{
    cin>>n;
    
    for(int k=16;;k--)
        if(check(k))break;
    return 0;
}

总结

知识点:二分,组合数
二分代码

这里的重点是如何取设置,r与l 然后怎么讲l与rmid 迭代

bool check(int k)
{
//这里需要理清怎么找边界
    int l=2*k,r=max(l,n);
    while(l<r)
    {
        int mid=l+r>>1;//找出l与r一半的数
        if(c(mid,k)>=n)
        {
            r=mid;
        }
        else{
        //需要理清这里是否要加1,否则会出现边界问题
            l=mid+1;
        }
    }
    if(c(r,k)!=n)return false;
    //1ll是为了防止爆出int的数据范围
    cout<<1ll*(r+1)*r/2+k+1<<endl;
    return true;
}
组合数
//这里也用到双指针
ll c(int a,int b)
{
    ll res=1;
    for(int i=a,j=1;j<=b;j++,i--)
        {
            res=res*i/j;
            if(res>n)return res;//当res比n大时无意义,直接返回
        }
    return res;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值