【总结】搜索的剪枝二分预处理和离散化等优化
2012-10-29 20:43
218 查看
送给圣诞夜的贺卡
位运算加速,因为集合的总元素个数较少,可以用位运算加速。
降序排序,使得越到后面S降得越快,让最后S很小,(参考黑书的剪枝那一节),因为到一般剪枝都在搜索的深层,所以容易被剪掉。反之,如果升序排序,则后面的S仍较大,不容易满足if (sum + S[u] <= ans)这个条件,也就不容易被剪掉。
极限化,如果剩余的都以最优情况决策,得到的解如果仍然不满足题意,则可以剪掉,达到加速。
手动调整决策方向,如此题中的没有冲突的,必须选择。
丛林探险
二分优化,时间总和不超过12000000,二分次数最多24次。如果能成功,就修改mid为s,这样还可以减少很多二分次数。剪枝都不需要。
软件下载
二分优化。这道题按照降序排序,因为所有的软件要下载完!!!(如果不一定下载完,当然就应该升序排序)这样就能尽早地剪掉大枝。
极限化,假设所有剩下的电脑刚好用掉所有能用的时间,如果还不能将所有下载时间进行完,剪掉。
核反应,和Soft一样的搜索方式,类似的剪枝,类似的排序。转换一下模型就是上面有一些小东西,要装到下面的一些大小固定的桶里,刚好装满,求能否成功。时间复杂度为O(m)
注意因为所有的都得装进去才能算作成功,因此不存在所谓的尽早找到可行解然后返回true的说法,只有尽早找出不符合题意的情况剪掉,因此上面的物品要降序排序,下面的桶升序排序。
Cake,离散化优化搜索,使复杂度只与存在的点有关。
等差数列
预处理优化,因为对于任意一个当前点u,它向后找到的下一个点v的权值都是固定的。
因此可以预处理出权值d最后出现的位置hash[d]。
再加上一个极限化的最优化剪枝。
位运算加速,因为集合的总元素个数较少,可以用位运算加速。
降序排序,使得越到后面S降得越快,让最后S很小,(参考黑书的剪枝那一节),因为到一般剪枝都在搜索的深层,所以容易被剪掉。反之,如果升序排序,则后面的S仍较大,不容易满足if (sum + S[u] <= ans)这个条件,也就不容易被剪掉。
极限化,如果剩余的都以最优情况决策,得到的解如果仍然不满足题意,则可以剪掉,达到加速。
手动调整决策方向,如此题中的没有冲突的,必须选择。
#include <cstring> #include <cstdio> #include <string> #include <algorithm> using std::sort; typedef long long ll; ll conflict[60]; long S[60]; bool wc[60]; long hash[60]; long v[60]; long n; long ans = 0; void dfs(long u,ll s,long sum) { if (u == n+1) { if (sum > ans) ans = sum; return; } if (sum + S[u] <= ans) return; if (!(s&conflict[u])) dfs(u+1,s|(1ll<<u),sum+v[u]); if (wc[u]) dfs(u+1,s,sum); } long getint() { long rs=0;bool sgn=1;char tmp; do tmp = getchar(); while (!isdigit(tmp)&&tmp!='-'); if (tmp=='-'){tmp=getchar();sgn=0;} do rs=(rs<<3)+(rs<<1)+tmp-'0'; while (isdigit(tmp=getchar())); return sgn?rs:-rs; } struct node { long v; long i; bool operator<(const node& n2)const { return v > n2.v; } }; node a[60]; int main() { freopen("gift.in","r",stdin); freopen("gift.out","w",stdout); n = getint(); for (long i=1;i<n+1;i++) { a[i].v = getint(); a[i].i = i; } sort(a+1,a+1+n); for (long i=1;i<n+1;i++) { hash[a[i].i] = i; v[i] = a[i].v; } for (long i=n;i>0;i--) S[i] = S[i+1]+v[i]; long u,v; while (scanf("%ld%ld",&u,&v)==2) { u = hash[u]; v = hash[v]; conflict[u] |= (1ll<<v); conflict[v] |= (1ll<<u); wc[u] = true; wc[v] = true; } dfs(1,0,0); printf("%ld",ans); return 0; }
丛林探险
二分优化,时间总和不超过12000000,二分次数最多24次。如果能成功,就修改mid为s,这样还可以减少很多二分次数。剪枝都不需要。
#include <cstdio> #include <string> #include <cstring> long s,t,k; long n; long mid; bool used[5010]; struct node { long ind; node* nxt; long c; long d; }; node* head[5010]; long getint() { long rs=0;bool sgn=1;char tmp; do tmp=getchar(); while (!isdigit(tmp)&&tmp-'-'); if (tmp=='-'){tmp=getchar();sgn=0;} do rs=(rs<<3)+(rs<<1)+tmp-'0'; while (isdigit(tmp=getchar())); return sgn?rs:-rs; } void insert(long a,long b,long c,long d) { node* nn = new node; nn -> ind = b; nn -> nxt = head[a]; nn -> c = c; nn -> d = d; head[a] = nn; } bool can(long u,long e,long s) { if (s > mid) return false; if (u == t) { if (mid > s) mid = s; return true; } for (node* vv=head[u];vv;vv=vv->nxt) { long v = vv->ind; if (!used[v]) { if (e-vv->c >= 0) { used[v] = true; if (can(v,e-vv->c,s+vv->d)) return true; used[v] = false; } } } return false; } int main() { freopen("forest.in","r",stdin); freopen("forest.out","w",stdout); n = getint(); long m = getint(); long sumd = 0; for (long i=1;i<m+1;i++) { long x = getint(); long y = getint(); long c = getint(); long d = getint(); insert(x,y,c,d); insert(y,x,c,d); sumd += d; } s = getint(); t = getint(); k = getint(); long ans = 0x3f3f3f3f; long l = 0; long r = sumd; while (l <= r) { mid = (l+r)>>1; memset(used,0,sizeof used); used[s] = true; if (can(s,k,0)) { if (ans > mid) ans = mid; r = mid-1; } else { l = mid+1; } } if (ans == 0x3f3f3f3f) printf("-1"); else printf("%ld",ans); return 0 ; }
软件下载
二分优化。这道题按照降序排序,因为所有的软件要下载完!!!(如果不一定下载完,当然就应该升序排序)这样就能尽早地剪掉大枝。
极限化,假设所有剩下的电脑刚好用掉所有能用的时间,如果还不能将所有下载时间进行完,剪掉。
#include <cstdio> #include <string> #include <cstring> #include <algorithm> #include <functional> using std::sort; #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) long m,n; long mid; long a[60]; long cnt[20]; bool used[60]; bool can(long u,long v,long left) { if ((n-u+1)*mid-cnt[u] < left) return false; if (v == m) return true; for (long i=1;i<m+1;i++) { if (!used[i]) { if (cnt[u] + a[i] <= mid) { used[i] = true; cnt[u] += a[i]; if (can(u,v+1,left-a[i])) return true; cnt[u] -= a[i]; used[i] = false; } else if (u < n) { used[i] = true; cnt[u+1] += a[i]; if (can(u+1,v+1,left-a[i])) return true; cnt[u+1] -= a[i]; used[i] = false; } else return false; } } return false; } long getint() { long rs=0;bool sgn=1;char tmp; do tmp = getchar(); while (!isdigit(tmp)&&tmp-'-'); if (tmp == '-'){tmp=getchar();sgn=0;} do rs=(rs<<3)+(rs<<1)+tmp-'0'; while (isdigit(tmp=getchar())); return sgn?rs:-rs; } int main() { freopen("soft.in","r",stdin); freopen("soft.out","w",stdout); n = getint(); m = getint(); long l = -0x3f3f3f3f; long r = 0; long sum = 0; for (long i=1;i<m+1;i++) { a[i] = getint(); r += a[i]; l = max(l,a[i]); } sum = r; sort(a+1,a+1+m,std::greater<long>() ); long ans = 0x3f3f3f3f; while (l <= r) { mid = (l+r) >> 1; memset(used,0,sizeof used); memset(cnt,0,sizeof cnt); used[1] = true; cnt[1] = a[1]; if (can(1,1,sum-a[1])) { if (ans > mid) ans = mid; r = mid-1; } else l = mid+1; } printf("%ld",ans); return 0; }
核反应,和Soft一样的搜索方式,类似的剪枝,类似的排序。转换一下模型就是上面有一些小东西,要装到下面的一些大小固定的桶里,刚好装满,求能否成功。时间复杂度为O(m)
注意因为所有的都得装进去才能算作成功,因此不存在所谓的尽早找到可行解然后返回true的说法,只有尽早找出不符合题意的情况剪掉,因此上面的物品要降序排序,下面的桶升序排序。
#include <cstdio> #include <map> #include <string> #include <iostream> #include <functional> #include <algorithm> using std::sort; using std::greater; using std::make_pair; using std::cin; using std::map; using std::string; string tmp; map<string,long> hash; char str[100][5] = {"H","He","Li","Be","B","C","N","O","F","Ne", "Na","Mg","Al","Si","P","S","Cl","Ar","K","Ca", "Sc","Ti","V","Cr","Mn","Fe","Co","Ni","Cu", "Zn","Ga","Ge","As","Se","Br","Kr","Rb","Sr", "Y","Zr","Nb","Mo","Tc","Ru","Rh","Pd","Ag", "Cd","In","Sn","Sb","Te","I","Xe","Cs","Ba", "La","Ce","Pr","Nd","Pm","Sm","Eu","Gd", "Tb","Dy","Ho","Er","Tm","Yb","Lu","Hf","Ta", "W","Re","Os","Ir","Pt","Au","Hg","Tl","Pb", "Bi","Po","At","Rn","Fr","Ra","Ac","Th","Pa", "U","Np","Pu","Am","Cm","Bk","Cf","Es","Fm"}; bool used[20]; long a[20]; long b[20]; long m,n; bool dfs(long u,long v) { if (v == m+1) { return true; } for (long i=1;i<m+1;i++) { if (!used[i]) { used[i] = true; if (a[u] >= b[i]) { a[u] -= b[i]; if (dfs(u,v+1)) return true; a[u] += b[i]; } used[i] = false; } else if (!a[u] && u<n) { used[i] = true; if (a[u+1] >= b[i]) { a[u+1] -= b[i]; if (dfs(u+1,v+1)) return true; a[u+1] += b[i]; } used[i] = false; } } return false; } int main() { freopen("nuclear.in","r",stdin); freopen("nuclear.out","w",stdout); for (long i=0;i<100;i++) hash.insert(make_pair(str[i],i+1)); while (scanf("%ld%ld",&m,&n) == 2) { memset(used,0,sizeof used); long sa = 0; long sb = 0; for (long i=1;i<m+1;i++) { cin >> tmp; b[i] = hash[tmp]; sb += b[i]; } for (long i=1;i<n+1;i++) { cin >> tmp; a[i] = hash[tmp]; sa += a[i]; } if (sa != sb) { printf("NO\n"); continue; } sort(b+1,b+1+m,greater<long>()); sort(a+1,a+1+n); if (dfs(1,1)) printf("YES\n"); else printf("NO\n"); } return 0; }
Cake,离散化优化搜索,使复杂度只与存在的点有关。
#include <cstdio> #include <string> #include <cstring> #include <algorithm> using std::min; using std::sort; using std::max; long n,k,m; long left[100010]; long right[100010]; long top[100010]; long bottom[100010]; long sum[100010]; long fa[100010]; struct POS { long x; long y; bool operator<(const POS& p2)const { if (x == p2.x) return y<p2.y; return x<p2.x; } }; POS pos[100010]; long getroot(long u) { if (fa[u] == u) return fa[u]; return fa[u] = getroot(fa[u]); } void merge(long a,long b) { long root = getroot(b); long rt = getroot(a); if (root != rt) { fa[rt] = root; bottom[root] = max(bottom[root],bottom[rt]); top[root] = min(top[root],top[rt]); left[root] = min(left[root],left[rt]); right[root] = max(right[root],right[rt]); sum[root] += sum[rt]; } } long getint() { long rs=0;bool sgn=1;char tmp; do tmp=getchar(); while (!isdigit(tmp)&&tmp-'-'); if (tmp=='-'){tmp=getchar();sgn=0;} do rs=(rs<<3)+(rs<<1)+tmp-'0'; while (isdigit(tmp=getchar())); return sgn?rs:-rs; } void next(long u,long &v) { v = u; while (v < k && pos[v+1].x == pos[v].x) { if (pos[v+1].y == pos[v].y+1) merge(v,v+1); v ++; } } void search() { long u1 = 1; long v1; next(u1,v1); long u2 = v1+1; long v2; next(u2,v2); while (v2 <= k) { if (pos[u2].x-pos[u1].x == 1) { long i = u1; long j = u2; while (i<v1+1 && j<v2+1) { if (abs(pos[i].y-pos[j].y)<=1) merge(i,j); if (pos[i].y<pos[j].y) i ++; else j ++; } } u1 = u2; v1 = v2; u2 = v1+1; next(u2,v2); } long ans = 0; for (long i=1;i<k+1;i++) { if (fa[i] == i) { if ((left[i]>1&&right[i]<m && top[i]>1&&bottom[i]<n) && ((right[i]-left[i]+1)*(bottom[i]-top[i]+1) == sum[i])) { ans ++; } } } printf("%ld",ans); } int main() { freopen("cake.in","r",stdin); freopen("cake.out","w",stdout); n = getint(); m = getint(); k = getint(); for (long i=1;i<k+1;i++) fa[i] = i; for (long i=1;i<k+1;i++) { pos[i].x = getint(); pos[i].y = getint(); } sort(pos+1,pos+1+k); long _k = 0; for (long i=1;i<k+1;i++) { if (pos[i].x!=pos[i-1].x||pos[i].y!=pos[i-1].y) { _k ++; left[_k] = right[_k] = pos[_k].y; top[_k] = bottom[_k] = pos[_k].x; sum[_k] = 1; } } k = _k; search(); return 0; }
等差数列
预处理优化,因为对于任意一个当前点u,它向后找到的下一个点v的权值都是固定的。
因此可以预处理出权值d最后出现的位置hash[d]。
再加上一个极限化的最优化剪枝。
#include <cstdio> #include <algorithm> using std::max; using std::lower_bound; using std::sort; long hash[10001110]; long num[5010]; long ans = 1; long n; void dfs(long u,long c,long v) { ans = max(ans,v); if (hash[num[u]+c]) { long i = hash[num[u]+c]; if (v+(n-i+1) <= ans) return; if (i>u && i<n+1 && num[i]-num[u] == c) dfs(i,c,v+1); } } long getint() { long rs=0;bool sgn=1;char tmp; do tmp=getchar(); while (!isdigit(tmp)&&tmp-'-'); if (tmp=='-'){tmp=getchar();sgn=0;} do rs=(rs<<3)+(rs<<1)+tmp-'0'; while (isdigit(tmp=getchar())); return sgn?rs:-rs; } int main() { freopen("num.in","r",stdin); freopen("num.out","w",stdout); n = getint(); for (long i=1;i<n+1;i++) { num[i] = getint(); } sort(num+1,num+1+n); for (long i=1;i<n+1;i++) { hash[num[i]] = i; } for (long i=1;i<n+1;i++) { ans = max(ans,hash[num[i]]-i+1); } for (long i=1;i<n+1;i++) { if (num[i] == num[i-1]) continue; for (long j=i+1;j<n+1;j++) { if (num[j] == num[j-1]) continue; dfs(j,num[j]-num[i],2); } } printf("%ld",ans); return 0; }
相关文章推荐
- HDOJ 2141 Can you find it?(二分搜索的优化+总结)
- 2144 砝码称重 2 用map离散化hasi+二分搜索
- 【题】【搜索(预处理剪枝)】NOIP2009 靶形数独
- 总结一下二分搜索
- hdu 4400 离散化+二分+BFS(暴搜剪枝还超时的时候可以借鉴一下)
- 搜索(剪枝优化):HDU 5113 Black And White
- 搜索与优化1搜索方法总结
- 搜索算法--线性搜索、二分搜索、内插搜索、剪枝搜索
- BZOJ 1082 栅栏 (二分 剪枝搜索)
- 寻找最大的K个数(二):快排优化和二分搜索
- uva 1601 poj 3523 Morning after holloween 万圣节后的早晨 (经典搜索,双向bfs+预处理优化+状态压缩位运算)
- 搜索的优化算法——剪枝
- hdu 2337 Escape from Enemy Territory (预处理+二分+搜索)
- uva 1601 poj 3523 Morning after holloween 万圣节后的早晨 (经典搜索,双向bfs+预处理优化+状态压缩位运算)
- 二分搜索及其思想(挑战例题总结)
- UOJ#9 浅谈在线仙人球嵌套动态网络路径剖分优化的分支定界贪心剪枝启发式迭代加深人工智能搜索决策算法解决问题
- 二分搜索相关总结
- n重循环的优化(二分搜索)
- 算法总结-二分搜索
- lucene 搜索优化,个人实战经验总结