长安大学第四届“迎新杯”程序设计竞赛 F 打铁的箱子【数学/进制思维/折半枚举】...

题目描述

作为彩虹岛上最擅长打铁的人,???今天一共打了?块大小为1的铁。为了保存这些铁块,他打算制作若干个箱子。其中,第1个箱子的容量为1(可装1块铁),之后每个箱子的容量都是它前一个箱子的容量的2倍。
但是他只会打铁,并不擅长制作箱子。制作完成后,他发现第1个箱子的容量是2(而不是1),之后每个箱子的容量都比它前一个箱子的容量的2倍少1,即第2个箱子的容量是3 = 2 × 2 − 1,第3个箱子的容量是5 = 3 × 2 − 1,第4个箱子的容量是9 = 5 × 2 − 1,依此类推。
作为一个勤俭节约的人,???想知道能否把这?块铁全部放入箱子中并使得所有装有铁块的箱子都装满了且没有空余。

输入描述:

输入第一行为一个整数?(1 ≤ ? ≤ 100),表示一共有?组数据。
对于每组测试数据:
第一行为一个整数?(1 ≤ ? ≤ 109),表示???一共打了?块铁。

输出描述:

对于每组测试数据,如果能够按照要求放进箱子里面则输出“YES”,否则输出“NO”。
示例1

输入

2
1
8

输出

NO
YES

说明

对于第一组样例,无法按照要求放入箱子中。
对于第二组样例,用容量为3和容量为5的箱子恰好能够放下8块铁。


 【分析】:每个背包的大小是个方程式f(n)=2^(n-1)+1 ,所以每个生成数是一个数加上其二进制上1的数目 。我们枚举1的数目i,最多不超过25,减去其并计算减去后的数的二进制1的数目n,如果n等于i就成功了。有2^(n-1)这个东西,它组成的数系能覆盖整个整数系, 但加了个1 。可知二进制中有几个1就加几个1。
【代码】:
#include <bits/stdc++.h>
using namespace std;
int main(){
    int T,min;cin>>T;
    while(T--)
    {
        int x,i;
        cin>>x;
        if(x>31) min=31;
        else min=x;
        for(i=1;i<=min;i++)
            if(__builtin_popcount(x-i)==i) {cout<<"YES";break;}
        if(i>min) cout<<"NO";
        cout<<'\n';
    }
 
    return 0;
}
二进制

 

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define INFLL 0x3f3f3f3f3f3f3f3f
#define x first
#define y second
#define LL long long
using namespace std;
const int maxn = 2e5+5;
int T;
int n;
LL a[35];
LL num1[2<<16];
LL num2[2<<16];
int p1 = 0;
int p2 = 0;
void dfs1(int l,int r,LL sum,int deep)
{
    if(deep==r+1){
        num1[p1++] = sum;
        return ;
    }
    dfs1(l,r,sum,deep+1);
    dfs1(l,r,sum+a[deep],deep+1);
}
void dfs2(int l,int r,LL sum,int deep)
{
    if(deep==r+1){
        num2[p2++] = sum;
        return ;
    }
    dfs2(l,r,sum,deep+1);
    dfs2(l,r,sum+a[deep],deep+1);
}

void init()
{
    a[0] = 2;
    for(int i = 1;i<31;i++){
        a[i] = a[i-1]*2-1;
    }
    dfs1(0,31/2,0,0);
    dfs2(31/2+1,30,0,31/2+1);
    sort(num1,num1+p1);
    sort(num2,num2+p2);
}
int main()
{
    init();
   scanf("%d",&T);
   while(T--){
        scanf("%d",&n);
        int falg = 0;
        for(int i = 0;i<p1;i++){

            if(num1[i]>n) break;
            int p = lower_bound(num2,num2+p2,n-num1[i])-num2;
           // cout<<num1[i]<<' '<<p<<' '<<num2[p]<<endl;
            if(p<p2&&num2[p]+num1[i]==n){
                falg = 1;
                break;
            }
        }
        if(falg){
            puts("YES");
        }
        else{
            puts("NO");
        }
   }
}
折半枚举

 

转载于:https://www.cnblogs.com/Roni-i/p/8053892.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值