• P3067 [USACO12OPEN]平衡的奶牛群(折半暴搜)


    暴搜无疑....

    首先考虑纯暴搜......

    考虑每一个数:

    1. 选在左边集合
    2. 选在右边集合
    3. 不选

    一共三种情况,用一个数组记录搜到的答案,所以暴搜是3^N的复杂度...直接死亡

    于是讲折半暴搜....

    把区间分为两半,对每一半进行dfs,用两个数组(vector)分别记录答案,于是复杂度就是3^(n/2)*2,在n<=20的情况下,能接受。

    但是

    如果这么简单,那就不是老师找的紫题的风格了....

    存储了两个区间的数组,怎么统计答案呢?

    话说折半暴搜的难点就在于统计答案了吧.....

    对于每个半区间进行排序,然后的暴力?

    那不可能,超时不说,还会有重复的记录....

    重复是因为:同时可能有一个数存在于左右两个区间中....很尴尬....

    这里是这一题的关键,怎么去重。

    这里使用了状压的思想,对于每一个搜到底的状态,我们记录一个01串,看哪些数被使用过,然后在统计答案的时候使用vis判重。

    这个vis真的是太妙了!!!!

    把两个数的状态或起来,看有没有重复使用的状态

    (太神了

    if(!vis[a[l].id|b[r].id])
    {
        ans++;
        vis[a[l].id|b[r].id]=1;
    }

    于是,剩下的就是要在数组中寻找那个关键点pos了.

    因为排序后数组单调,所以找到这个pos之后,后面所有的答案都可能符合,只要去重就行了。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=22;
    int n;
    int ans;
    int w[maxn<<1],vis[1<<maxn];
    struct node
    {
        int sum,id;
    };
    int cnta,cntb;
    node a[1<<maxn];
    node b[1<<maxn];
    int mid;
    void dfs1(int now,int sum,int id)
    {
        if(now==mid+1)
        {
            a[++cnta].sum=sum;
            a[cnta].id=id;
            return;
        }
        
        
        {
            dfs1(now+1,sum,id);
            dfs1(now+1,sum+w[now],id+(1<<(now-1)));
            dfs1(now+1,sum-w[now],id+(1<<(now-1)));
        }
    }
    void dfs2(int now,int sum,int id)
    {
        if(now==n+1)
        {
            b[++cntb].sum=sum;
            b[cntb].id=id;
            return;
        }
        
        {
            dfs2(now+1,sum,id);
            dfs2(now+1,sum+w[now],id+(1<<(now-1)));
            dfs2(now+1,sum-w[now],id+(1<<(now-1)));
        }
    }
    
    bool cmp1(node a,node b)
    {
        return a.sum<b.sum;
    }
    bool cmp2(node a,node b)
    {
        return a.sum>b.sum;
    }
    
    
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&w[i]);
        }
        mid=n>>1;
        dfs1(1,0,0);
        dfs2(mid+1,0,0);
        sort(a+1,a+cnta+1,cmp1);
        sort(b+1,b+cntb+1,cmp2);
        int l=1,r=1;
        while(l<=cnta&&r<=cntb)
        {
            while(r<=cntb&&-a[l].sum<b[r].sum)r++;
            int pos=r;
            while(r<=cntb&&-a[l].sum==b[r].sum)
            {
                if(!vis[a[l].id|b[r].id])
                {
                    ans++;
                    vis[a[l].id|b[r].id]=1;
                }
                r++;
            }
            if(l<cnta&&a[l].sum==a[l+1].sum)
            r=pos;
            l++;
        }
        printf("%d",ans-1);
        return 0;
    }

    (完)

  • 相关阅读:
    将数据加载到内存中
    反射,Java开发者必须跨越的坎
    Eclipse中Java文件图标由实心J变成空心J的问题
    Maven项目 解决cannot be read or is not a valid ZIP file问题
    MyEclipse中SVN
    常用的 正则验证等
    前台传递数组后台通过json字符串承接和处理
    img标签图片与图片背景的区别和使用场景
    数据库的特性和事务的特性
    redis在EOS7上面进行开启和停止的命令
  • 原文地址:https://www.cnblogs.com/ajmddzp/p/11582078.html
Copyright © 2020-2023  润新知