折半搜索
折半爆搜
千篇一律
基本数据范围在 \(n<=40\) 然后要搜索所有集合状态,
折半爆搜即搜前一半,再搜后一半(顺便统计答案)总复杂度由\(O(2^{40})\)降到 \(O(2^{20})\)
Incredible Cows
把\(n\)数分成两堆,使得两堆和的差最小。
#include<cstdio>
#include<cmath>
#include<vector>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
const int N = 200005;
int T, n, a[2][N], m[2];
int h[N], f2[N], t[2], ans;
void dfs(int flag, int x, int y) {
if (x > m[flag]) {
if (flag) f2[t[flag]] = y;
else h[t[flag]] = y;
t[flag]++;
return;
}
dfs(flag, x + 1, abs(y + a[flag][x]));
dfs(flag, x + 1, abs(y - a[flag][x]));
}
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
t[0] = t[1] = 0;
m[0] = (n + 1) / 2;
m[1] = n / 2;
for (int i = 1; i <= m[0]; i++) scanf("%d", &a[0][i]);
for (int i = 1; i <= m[1]; i++) scanf("%d", &a[1][i]);
dfs(0, 1, 0);
dfs(1, 1, 0);
sort(h, h + t[0]);
sort(f2, f2 + t[1]);
ans = 0x3f3f3f3f;
for (int i = 0; i < t[0]; i++) {
int k = lower_bound(f2, f2 + t[1], h[i]) - f2;
if (k < t[1]) ans = min(ans, f2[k] - h[i]);
}
for (int i = 0; i < t[1]; i++) {
int k = lower_bound(h, h + t[0], f2[i]) - h;
if (k < t[0]) ans = min(ans, h[k] - f2[i]);
}
printf("%d\n", ans);
}
return 0;
}
世界冰球锦标赛
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=(1<<21);
inline LL read() {
LL x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n,mid;
LL m,a[2][45],l[N],r[N],t[2],ans;
void dfs(int f,int x,LL sum) {
if(f&&x>n-mid) {
r[++t[1]]=sum;
return;
}
if(f==0&&x>mid) {
l[++t[0]]=sum;
return;
}
dfs(f,x+1,sum);
dfs(f,x+1,sum+a[f][x]);
}
int main() {
n=read();m=read();
mid=(n+1)/2;
for(int i=1;i<=mid;i++) a[0][i]=read();
for(int i=1;i<=n-mid;i++) a[1][i]=read();
dfs(0,1,0);
dfs(1,1,0);
sort(l+1,l+1+t[0]);
sort(r+1,r+1+t[1]);
for(int i=1;i<=t[0];i++) {
int k=upper_bound(r+1,r+1+t[1],m-l[i])-r-1;
ans+=k;
}
printf("%lld\n",ans);
return 0;
}
9.7模拟赛T2
\([1,n]\)正着搜出来串存在\(Map\)里,然后\([n+1,2n]\)搜出来串反着连接从\(Map\)里找相同的贡献答案
#include <unordered_map>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
int n;
char s[40];
LL ans;
unordered_map<string,int>mp;
bool vis[40];
void dfs1(int x) {
if(x==n+1) {
string s1,s2;
for(int i=1;i<=n;i++)
if(vis[i]) s1+=s[i];
for(int i=n;i>=1;i--)
if(!vis[i]) s2+=s[i];
mp[s1+'#'+s2]++;
return;
}
vis[x]=0;dfs1(x+1);
vis[x]=1;dfs1(x+1);
}
void dfs2(int x) {
if(x==2*n+1) {
string s1,s2;
for(int i=n+1;i<=2*n;i++)
if(vis[i]) s1+=s[i];
for(int i=2*n;i>=n+1;i--)
if(!vis[i]) s2+=s[i];
ans+=mp[s2+'#'+s1];
return;
}
vis[x]=0;dfs2(x+1);
vis[x]=1;dfs2(x+1);
}
int main() {
scanf("%d%s",&n,s+1);
dfs1(1);
dfs2(n+1);
printf("%lld\n",ans/2);
return 0;
}
11.9 模拟赛 hash_table
很类似上面
正着搜出来前一半串存在哈希表里,然后搜出来后一半从哈希表里找相反数贡献答案
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=7;
const int inf=2147483647;
inline int read() {
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
void write(int x) {
static short st[33];short tp=0;
do st[++tp]=x%10,x/=10;while(x);
while(tp) putchar('0'|st[tp--]);
putchar('\n');
}
int n,m;
int k[N],p[N],ans;
int Pow[155][7];
const int P=6974895;
struct Hash_Tale {
int hd[P+5],to[P+5],nxt[P+5],tot,val[P+5];
inline void ins(int x) {
int v=(x%P+P)%P;
for(int i=hd[v];i;i=nxt[i]) {
if(to[i]==x) {
++val[i];return;
}
}
to[++tot]=x;val[tot]=1;nxt[tot]=hd[v];hd[v]=tot;
}
inline int query(int x) {
int v=(x%P+P)%P;
for(int i=hd[v];i;i=nxt[i])
if(to[i]==x)
return val[i];
return 0;
}
}Hash;
void dfs1(int x,int sum) {
if(x==(n/2)+1) {
Hash.ins(sum);
return;
}
for(int i=1;i<=m;i++)
dfs1(x+1,sum+k[x]*Pow[i][p[x]]);
}
void dfs2(int x,int sum) {
if(x==n+1) {
ans+=Hash.query(sum);
return;
}
for(int i=1;i<=m;i++)
dfs2(x+1,sum-k[x]*Pow[i][p[x]]);
}
int main() {
n=read();m=read();
for(int i=1;i<=n;i++) {
k[i]=read();p[i]=read();
}
for(int i=1;i<=m;i++)
for(int j=0;j<=4;j++)
Pow[i][j]=j?Pow[i][j-1]*i:1;
dfs1(1,0);
dfs2(n/2+1,0);
write(ans);
return 0;
}
CF888E Maximum Subsequence
给一个数列和\(m\),在数列任选若干个数,使得他们的和对\(m\)取模后最大\(1<=n<=35\)
#include <set>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=40;
inline int read() {
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n,m,a[N],mid;
set<int>st;
void dfs1(int x,int sum) {
if(x==mid+1) {
st.insert(sum);
return;
}
dfs1(x+1,sum);
dfs1(x+1,(sum+a[x])%m);
}
int ans;
void dfs2(int x,int sum) {
if(x==n+1) {
set<int>::iterator it=st.upper_bound(m-1-sum);
it--;
ans=max(ans,sum+*it);
return;
}
dfs2(x+1,sum);
dfs2(x+1,(sum+a[x])%m);
}
int main() {
n=read();m=read();
mid=n/2;
for(int i=1;i<=n;i++) a[i]=read();
dfs1(1,0);
dfs2(mid+1,0);
printf("%d\n",ans);
return 0;
}