传送门: [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