折半搜索——P3067 [USACO12OPEN]Balanced Cow Subsets G+P4799 [CEOI2015 Day2] 世界冰球锦标赛

传送门: [CEOI2015 Day2] 世界冰球锦标赛 - 洛谷(折半搜索模板题目)

思路:先考虑纯暴力的做法,每一个数都有两种选择,选或不选,时间复杂度为O(2^40)

折半搜索通常就是可以将时间复杂度在O(2^30)~O(2^40)降低到O(2^20)左右

1.将原数组直接分成前后两半,比如{1,2,3,4} 就分成{1,2}和{3.4}

2.两个数组分别找出所有能组合出来的数{1,2}可以得到{0,1,2,3}, {3.4}可以得到{0,3,4,7}, (这里的时间复杂度就是O(2^20),每一个数都有选或不选两种选择)

3.上面得到两个新的数组a和b,假设现在对a进行排序,遍历数组b找到每一个b[i]和m的差值的绝对值 t ,  统计在a中小于等于t的数的个数,因为a是有序的,说可以通过二分来进行查找

代码:

#include
using namespace std;
#define int long long
const int N=1e7+10,mod=1e9+7;
int a[N],b[N];
int cnta,cntb;
int n,m;
int d[N];
void dfs(int l,int r,int sum,int p[],int &cnt)
{
    if(sum>m) return ;
    if(l>r)
    {
        p[++cnt]=sum;
        return ;
    }
    dfs(l+1,r,sum+d[l],p,cnt);
     dfs(l+1,r,sum,p,cnt);
}

signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&d[i]);

    int mid=n/2;
    dfs(1,mid,0,a,cnta);
    dfs(mid+1,n,0,b,cntb);

    sort(a+1,a+cnta+1);

    int sum=0;
    for(int i=1;i<=cntb;i++)
    {
        sum+=upper_bound(a+1,a+cnta+1,m-b[i]) -a-1;
    }
   printf("%lld",sum);
	return 0;
}

传送门:[USACO12OPEN]Balanced Cow Subsets G - 洛谷

思路:

注意点:按照题目的意思是求有多少个不同子集,而不是有多少中不同的划分方案,比如说

{1,2,2,3,4,6}这个集合可以划分成{3,6}和{1,2,2,4}  还有{1,2,6}和{2,3,4} ,但是原集合对于答案的贡献依旧是1

最暴力的情况下要考虑到所有的情况,那么对于每一头奶牛都有三种选择

1.加入第一组

2.加入第二组

3.都不加入

时间复杂度为O(3^n)

像上面一样先分成两半,然后分别搜索则时间复杂度为O(3^(n/2))按照题目的就变成O(3^10)

分成两半的时候,假设前一半在第一组放入的是a,在第二组放入的是b

                                    后一半在第一组放入的是c,在第二组放入的是d

那么可以得到有a+c=b+d,考虑到两组要分开处理可以变成a-b=d-c

这下就只需要维护a-b的值

同时还要考虑上面说过的注意点,重复的问题,这里n最多只有20,可以通过位运算状态压缩的方式存储不同的abcd组合出的状态,然后存入哈希表中,最后统计有多少种不同的的哈希值就可以了

代码:

#include
using namespace std;
#define int long long
const int N=2e6+10,mod=1e9+7;
int n;
int a[22];
int d[N],cnt;
vector p[N];
mapb;
void dfs1(int l,int r,int sum,int t)
{
    if(l>r)
    {
        if(b[sum]==0) b[sum]=++cnt;
        p[b[sum]].push_back(t);

        return;
    }
    dfs1(l+1,r,sum+a[l],t|(1<<(l-1)));
    dfs1(l+1,r,sum-a[l],t|(1<<(l-1)));
    dfs1(l+1,r,sum,t);
}
void dfs2(int l,int r,int sum,int t)
{
    if(l>r)
    {
        int c=b[sum];
        if(c!=0)
        {
            for(int i=0;i

你可能感兴趣的:(搜索,深度优先,算法)