bzoj2679: [Usaco2012 Open]Balanced Cow Subsets(折半搜索)

本文探讨了在一组整数中寻找所有可能的平衡子集,即这些子集可以被划分成两个部分,使得两部分的元素之和相等。通过使用深度优先搜索和动态规划方法,文章详细介绍了两种解决方案:一种是基于状态压缩的递归搜索,另一种是利用折半搜索和双指针技巧来优化计算过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2679: [Usaco2012 Open]Balanced Cow Subsets

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 462  Solved: 197
[Submit][Status][Discuss]

Description

Farmer John's owns N cows (2 <= N <= 20), where cow i produces M(i) units of milk each day (1 <= M(i) <= 100,000,000). FJ wants to streamline the process of milking his cows every day, so he installs a brand new milking machine in his barn. Unfortunately, the machine turns out to be far too sensitive: it only works properly if the cows on the left side of the barn have the exact same total milk output as the cows on the right side of the barn! Let us call a subset of cows "balanced" if it can be partitioned into two groups having equal milk output. Since only a balanced subset of cows can make the milking machine work, FJ wonders how many subsets of his N cows are balanced. Please help him compute this quantity.

给出N(1≤N≤20)个数M(i) (1 <= M(i) <= 100,000,000),在其中选若干个数,如果这几个数可以分成两个和相等的集合,那么方案数加1。问总方案数。

Input

 Line 1: The integer N. 
 Lines 2..1+N: Line i+1 contains M(i).

Output

* Line 1: The number of balanced subsets of cows.

Sample Input

4 1 2 3 4
INPUT DETAILS: There are 4 cows, with milk outputs 1, 2, 3, and 4.

Sample Output

3
OUTPUT DETAILS: There are three balanced subsets: the subset {1,2,3}, which can be partitioned into {1,2} and {3}, the subset {1,3,4}, which can be partitioned into {1,3} and {4}, and the subset {1,2,3,4} which can be partitioned into {1,4} and {2,3}.

HINT

 

Source

 

/*
判断能否划分为两个相等集合时用dp RE了
*/
#include<bits/stdc++.h>
 
 #define N 30
 #define M 3111111
 #define mod 2333333
 
 using namespace std;
 int n,m,ans,cnt,flag;
 int a[N],vis[N],V[M];
 int cur[N],sum[N];
 
 inline int read()
 {
     int x=0,f=1;char c=getchar();
     while(c>'9'||c<'0'){if(x=='-')f=-1;c=getchar();}
     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
     return x*f;
 }
 
 bool dfs2(int cur[],int k,int val,int n)
 {
     if(n==2 && cur[1]!=cur[2]) return false;  
     if(flag) return true;
     if(val==sum[n]-val) {flag=1;return true;}
     if(k==n && !flag) return false;
     for(int i=k+1;i<=n;i++)
     dfs2(cur,i,val+cur[i],n),dfs2(cur,i,val,n);
     if(!flag)return false;
 }
 
 bool judge()
 {
     int cnt_=0,S=1;
     memset(cur,0,sizeof cur);
    memset(sum,0,sizeof sum);
     for(int i=1;i<=n;i++) if(vis[i]) cur[++cnt_]=a[i],sum[cnt_]=sum[cnt_-1]+cur[cnt_];
     sort(cur+1,cur+cnt_+1);
     for(int i=1;i<=cnt_;i++) S+=S*33+cur[i],S%=mod;
     if(V[S]) return false;V[S]=1;flag=0;
     if(sum[cnt_]%2) return false; 
     if(dfs2(cur,0,0,cnt_)) return true;
     return false;
     
 }
 
 void dfs(int lim,int k,int tot)
 {
     if(tot==lim)
     {
         if(judge()) ans++;
         return;
    }
     if(k>n) return;
     for(int i=k+1;i<=n;i++)
     {
         if(vis[i]) continue;
         vis[i]=1;dfs(lim,k+1,tot+1);
         vis[i]=0;
    }
 }
 
 int main()
 {
    //freopen("ly.in","r",stdin);
     n=read();
     for(int i=1;i<=n;i++) a[i]=read();
     cnt=2;
    while(cnt<=n)
    {
        memset(vis,0,sizeof vis);
        dfs(cnt,0,0);
        cnt++;
    }
    printf("%d\n",ans);
    return 0;
 }
24暴搜

 

/*
折半搜索
枚举每个数如何选择,放入A就加,放入B就减 
状压判断每个数的具体选择状态
最后双指针扫统计答案  若集合A的和 + 集合B的和为0那么就说明这两个集合构成的答案合法 
*/
#include<bits/stdc++.h>

#define N 22
#define ll long long

using namespace std;
int n,v[N<<1],maxdep,cnta,cntb;
bool vis[1<<N];
ll ans;
struct node{    
    int state,x;
}a[1<<N],b[1<<N];
inline bool cmp1(node a,node b){return a.x<b.x;}
inline bool cmp2(node a,node b){return a.x>b.x;}

inline int read()
 {
     int x=0,f=1;char c=getchar();
     while(c>'9'||c<'0'){if(x=='-')f=-1;c=getchar();}
     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
     return x*f;
 }

void dfs(int dep,int sum,int now,int flag)
{
    if(dep==maxdep+1)
    {
        if(!flag)
            a[++cnta].x=sum,a[cnta].state=now;
        else
            b[++cntb].x=sum,b[cntb].state=now;
        return;
    }
    dfs(dep+1,sum,now,flag);
    dfs(dep+1,sum+v[dep],now | (1<<(dep-1)),flag);
    dfs(dep+1,sum-v[dep],now | (1<<(dep-1)),flag);
}
int main()
{
    n=read();
    for(int i=1; i<=n; i++)v[i]=read();
    maxdep=n/2;dfs(1,0,0,0);
    maxdep=n;  dfs(n/2+1,0,0,1);
    sort(a+1,a+1+cnta,cmp1);
    sort(b+1,b+1+cntb,cmp2);
    
    int l=1,r=1;
    while(l<=cnta&&r<=cntb)
    {
        while(-a[l].x<b[r].x&&r<=cntb)r++;
        int pos=r;
        while(r<=cntb&&-a[l].x==b[r].x)
        {
            if(!vis[a[l].state | b[r].state])
            {
                vis[a[l].state | b[r].state]=1;
                ans++;
            }r++;
        }
        if(l<cnta&&a[l].x==a[l+1].x)r=pos;
        l++;
    }
    printf("%lld\n",ans-1);//减去空集 
    return 0;
}

 

转载于:https://www.cnblogs.com/L-Memory/p/9887352.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值