折半搜索+状态压缩【P3067】 [USACO12OPEN]平衡的奶牛群Balanced Cow S…

Description

给n个数,从中任意选出一些数,使这些数能分成和相等的两组。

求有多少种选数的方案。

Input

\(1\)行:一个整数\(N\)

\(2\)\(N+1\)行,包含一个整数\(m_i\)

Output

一行:平衡的集合的个数.

看到题的一瞬间数据范围?

\(N \leq 20?\)状压!

明显直接做过不去,选择折半搜索.

折半搜索的话会有三种情况

  • 一.选择当前位置
  • 二.选择当前位置,给第一组.
  • 三.选择当前位置,给第二组.

然后直接跑折半搜索+状压即可.

存储类似链式前向星,应该不是很难理解,就不过多解释了.

然后就枚举状态即可,可是直接枚举到\(2^n-1\)显然会\(T\)掉.

由于我们后半截的状态已知,所以说,我们只需要枚举前一半的状态即可.

注意要\(sort\)找到两边力气值相等的.

其他的就不太难理解了,如果不能理解的话可以私信我 qwq.

代码

#include<cstdio>
#include<cctype>
#include<algorithm>
#define N 10000008
#define R register
using namespace std;
inline void in(int &x)
{
    int f=1;x=0;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s)){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,w[28],mid,head[N];
struct cod{int u,val;}e[200008],edge[N];
int v[N],ans,tot,ttt,sta,cnt;
bool vis[2500000];
void dfs1(int dep,int sum,int state)
{
    if(dep>mid)
    {
        edge[++tot].u=head[state];
        edge[tot].val=sum;
        head[state]=tot;
        return;
    }
    dfs1(dep+1,sum,state);
    dfs1(dep+1,sum+w[dep],state|(1<<(dep-1)));
    dfs1(dep+1,sum-w[dep],state|(1<<(dep-1)));
}
void dfs2(int dep,int sum,int state)
{
    if(dep>n)
    {
        e[++ttt].u=state;
        e[ttt].val=sum;
        return;
    }
    dfs2(dep+1,sum,state);
    dfs2(dep+1,sum+w[dep],state | (1<<(dep-1)));
    dfs2(dep+1,sum-w[dep],state | (1<<(dep-1)));
}
inline bool ccp(const cod&a,const cod&b)
{
    return a.val<b.val;
}
int main()
{
    in(n);mid=(n+1)>>1;sta=(1<<n)-1;
    for(R int i=1;i<=n;i++)in(w[i]);
    dfs1(1,0,0);dfs2(mid+1,0,0);
    sort(e+1,e+ttt+1,ccp);
    for(R int i=0;i<=(1<<mid);i++)
    {
        R int cnt=0;
        for(R int j=head[i];j;j=edge[j].u)
            v[++cnt]=edge[j].val;
        sort(v+1,v+cnt+1);
        R int pos=1;
        if(v[1]>e[ttt].val)break;
        for(R int j=1;j<=ttt;j++)
        {
            while(pos<=cnt and v[pos]<e[j].val)pos++;
            if(pos>cnt)break;
            if(v[pos]==e[j].val)
                vis[i|e[j].u]=true;
        }
    }
    for(R int i=1;i<=sta;i++)
        if(vis[i])ans++;
    printf("%d",ans);
}
/*
10
5 8 16 17 25 83 24 7 8 20

89
*/

转载于:https://www.cnblogs.com/-guz/p/9826717.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值