Codeforces 949E Binary Cards 乱搞

题意

给出n个需要表示的数,你需要用最少的 2k 2 k 2k − 2 k ,使得能拼出所有需要表示的数。输出方案。
n,|Ai|<=100000 n , | A i | <= 100000

分析

不难发现 2k 2 k 2k − 2 k 里面最多只要选一个,因为选两个 2k 2 k 一定没有选 2k 2 k 2k+1 2 k + 1 优秀,选 2k 2 k 2k − 2 k 一定没有选 2k 2 k 2k+1 − 2 k + 1 优秀。
那么我们可以考虑缩小规模:
当所有数都是偶数的时候,把每个数除以2然后递归下去。
否则的话就有选1和-1两种情况,只要枚举每一种情况然后递归下去就好了。
在递归到每一层的时候去一下重,那么第i层的数的数量就不超过 Ai2i A i 2 i
于是总的复杂度就是O(nlogn)。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=200005;
const int inf=1000000000;

int n,a[N],f[20][N],ans,tot[20],A[20],top,stack[20];

void solve(int dep,int step)
{
    if (dep==19) return;
    sort(f[dep]+1,f[dep]+tot[dep]+1);
    tot[dep]=unique(f[dep]+1,f[dep]+tot[dep]+1)-f[dep]-1;
    int s1=0,s0=0;
    for (int i=1;i<=tot[dep];i++)
        if (f[dep][i]!=0&&f[dep][i]%2==0) s0++;
        else if (f[dep][i]%2!=0) s1++;
    if (!s0&&!s1)
    {
        if (step<ans)
        {
            ans=step;
            for (int i=1;i<=step;i++) A[i]=stack[i];
        }
        return;
    }
    if (!s1)
    {
        tot[dep+1]=0;
        for (int i=1;i<=tot[dep];i++)
            if (f[dep][i]/2!=0) f[dep+1][++tot[dep+1]]=f[dep][i]/2;
        solve(dep+1,step);
    }
    else
    {
        tot[dep+1]=0;stack[++top]=1<<dep;
        for (int i=1;i<=tot[dep];i++)
            if (f[dep][i]%2==0&&f[dep][i]/2!=0) f[dep+1][++tot[dep+1]]=f[dep][i]/2;
            else if (f[dep][i]%2!=0) f[dep+1][++tot[dep+1]]=(f[dep][i]-1)/2;
        solve(dep+1,step+1);top--;
        tot[dep+1]=0;stack[++top]=-(1<<dep);
        for (int i=1;i<=tot[dep];i++)
            if (f[dep][i]%2==0&&f[dep][i]/2!=0) f[dep+1][++tot[dep+1]]=f[dep][i]/2;
            else if (f[dep][i]%2!=0) f[dep+1][++tot[dep+1]]=(f[dep][i]+1)/2;
        solve(dep+1,step+1);top--;
    }
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+n+1);n=unique(a+1,a+n+1)-a-1;
    tot[0]=n;
    for (int i=1;i<=n;i++) f[0][i]=a[i];
    ans=20;solve(0,0);
    printf("%d\n",ans);
    for (int i=1;i<=ans;i++) printf("%d ",A[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值