搜索【折半搜索】【双向BFS】【dfs剪枝优化+A*/IDA*】
折半搜索
meet in mid
对于枚举指数级别大的,分成2个部分枚举,再考虑进行答案的拼接合并。从\(O(a^b)-->O(a^{b/2}*2)\)。拼接的方式可以是hash表查询,bitset标记,双指针单调性移动。
【https://www.luogu.com.cn/problem/P5691】解方程,\(sigma(ki*xi^{pi})=0\),其中xi是需要你求出的不同解法,给出[1,m]的区间限制。n<=6.
正常枚举xi的每项\(O(150^6)\),考虑分成左区间和右区间分别求sum然后合并,就可以了。
积累实现方式:对于一个很大的val,我要记录出现次数,(map),hash表查询。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
ll x=0,h=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')h=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*h;
}
const int N=300+10;
const int mod=5e6+7;
int P[153][9];
int n,m,k[9],p[9],mxp,table[mod+10],t[mod+10];
ll ans;
inline int Hash(int x)
{
int v=(x>0)?(x%mod):((-x)%mod);
while(t[v]&&table[v]!=x)
{
v++;if(v>=mod)v=0;
}
return v;
}
//int cnt=0;
inline void dfs1(int num,int val)
{
if(num==(n>>1)+1)
{
// ++cnt;
// chu("%d %d:%d\n",(n>>1)+1,cnt,val);
int pos=Hash(val);t[pos]++;table[pos]=val;
// chu("hash(%d):t[%d]:%d\n",val,pos,t[pos]);
return;
}
_f(i,1,m)dfs1(num+1,val+P[i][p[num]]*k[num]);
}
inline void dfs2(int num,int val)
{
if(num==n+1)
{
int pos=Hash(val);ans+=t[pos];return;
}
_f(i,1,m)dfs2(num+1,val-P[i][p[num]]*k[num]);
}
int main()
{
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
n=re(),m=re();
_f(i,1,n)k[i]=re(),p[i]=re(),mxp=max(mxp,p[i]);
_f(i,1,m)
_f(j,0,mxp)
if(j==0)P[i][j]=1;
else P[i][j]=P[i][j-1]*i;
dfs1(1,0);
// _f(i,0,5e6+6)
// if(t[i])chu("t[%d]:%d\n",i,t[i]);
dfs2((n>>1)+1,0);
chu("%lld",ans);
return 0;
}
/*
3
150
1 2
-1 2
1 2
*/
【https://www.luogu.com.cn/problem/P3067】给出n个数,求把n个数取出若干个,若干个可以分成数值相同的2组的取法方案数。(n<=20)
对于一个数,有3种状态【1】选到A集合【2】选到B集合【3】不选,分别在累计sum的时候分+-号就行。问题变成把数分到3个集合让其中AB集合数sum相同的方案数,就是从左区间选出x个,右区间选出y个,让sum_x+sum_y=0的数集方案数,统计可以出现sum的个数然后求和就行。但是会出现重复,比如说同一个数集合,可能其中正负取不同都合法,但是只可以算一次-->(1)vis标记去掉重复(2)直接二进制数组记录所有状态,不累加方案而是去更新拼接的状态数,最后统计。
\(O(3^n*2)\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
ll x=0,h=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')h=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*h;
}
const int N=(1<<10)+100;
int a[22],n,mid;
ll ans;
bitset<N>vis[N],tmp;
map<int,bitset<N> >mp;
inline void ldfs(int now,int cost,int id)
{
if(now==mid+1)
{
mp[cost].set(id,1);return;
}
ldfs(now+1,cost+a[now],id|(1<<(now-1)));
ldfs(now+1,cost-a[now],id|(1<<(now-1)));
ldfs(now+1,cost,id);
}
inline void rdfs(int now,int cost,int id)
{
if(now==n+1)
{
if(mp[cost].count()==0)return;//没有
tmp=mp[cost];//可以选择的数集合
tmp&=(~vis[id]);
ans+=tmp.count();
vis[id]|=tmp;
return;
}
rdfs(now+1,cost+a[now],id|(1<<(now-mid-1)));
rdfs(now+1,cost-a[now],id|(1<<(now-mid-1)));
rdfs(now+1,cost,id);
}
int main()
{
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
n=re();mid=n>>1;
_f(i,1,n)a[i]=re();
ldfs(1,0,0);
rdfs(mid+1,0,0);
chu("%lld",ans-1);
return 0;
}
/*
4
1
2
3
4
*/
双向BFS
多起点互相奔赴,用size提前记录保证每个状态只更新了一轮。
https://blog.csdn.net/qq_29169749/article/details/51420097
在紫书上的搜索
*AandDFS
主要是剪枝和估价函数的计算。