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;
}

(完)

猜你喜欢

转载自www.cnblogs.com/ajmddzp/p/11582078.html