• [CSP校内集训]building(折半搜索)


    题意

    (n)个东西,每个可以选择不取、取价值(a_i)、取价值(b_i),求恰好凑出价值(x)的方案数(,(nleq 25,a_i,b_i,xleq 10^{13}))

    思路

    这种看起来很简单暴力的题只用考虑爆搜就完了,但是直接爆搜是(O(3^n))会飞

    折半搜索,分成将(n)分成两部分,前一半搜出来的方案数开个map存起来,后一半搜出来的方案数乘上前面即可,复杂度优化至(O(3^{n/2}logx))

    这样做相当于将原来一个个加答案变成一次加一堆

    Code

    #include<bits/stdc++.h>
    #define N 26
    #define lowbit(x) ((x)&(-(x)))
    #define Min(x,y) ((x)<(y)?(x):(y))
    #define Max(x,y) ((x)>(y)?(x):(y))
    using namespace std;
    typedef long long ll;
    int n,half;
    ll x,a[N],b[N],ans;
    map<ll,ll> mp;//状态的出现次数 
    
    template <class T>
    void read(T &x)
    {
    	char c; int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
    }
    void DFS(int step,ll now)
    {
    	if(step==half+1) {++mp[now];return;}
    	if(now>x) return;
    	DFS(step+1,now);
    	DFS(step+1,now+a[step]);
    	DFS(step+1,now+b[step]);
    }
    void dfs(int step,ll now)
    {
    	if(step==n+1)
    	{
    		if(now<=x) ans+=mp[x-now];
    		return; 
    	}
    	if(now>x) return;
    	dfs(step+1,now);
    	dfs(step+1,now+a[step]);
    	dfs(step+1,now+b[step]);
    }
    int main()
    {
    	freopen("building.in","r",stdin);
    	freopen("building.out","w",stdout);
    	read(n);read(x);
    	for(int i=1;i<=n;++i)
    	{
    		read(a[i]);
    		ll c=a[i],cnt=0;
    		while(c)
    		{
    			++cnt;
    			c-=lowbit(c);
    		}
    		b[i]=(1LL<<cnt);
    	}
    	if(n==1)
    	{
    		cout<<(0==x)+(a[1]==x)+(b[1]==x)<<endl;
    		return 0;
    	}
    	half=n/2;
    	DFS(1,0);
    	dfs(half+1,0);
    	cout<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    开发工具 内存,性能检测工具
    数据结构 二分法查找
    C语言 goto语句
    C语言 结构体中的零长度数组
    C语言 结构体(联合体)对齐规则
    C++ STL堆操作
    C语言 sscanf用法详解
    Sword 内核队列二
    Sword 内核队列一
    Sword libcurl回调函数相关知识
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11804901.html
Copyright © 2020-2023  润新知