双向搜索--从初态和终态出发各搜索一般状态,产生两棵深度减半的搜索树,在中间交会、组合成最终的答案
例题:送礼物(牛客网)
题目描述:
作为惩罚,GY被遣送去帮助某神牛给女生送礼物(GY:貌似是个好差事)但是在GY看到礼物之后,他就不这么认为了。
某神牛有N个礼物,且异常沉重,但是GY的力气也异常的大(-_-b),他一次可以搬动重量和在w(w≤231−1)以下的
任意多个物品。GY希望一次搬掉尽量重的一些物品,请你告诉他在他的力气范围内一次性能搬动的最大重量是多少。
思路:
起初的思路是折半枚举+状态压缩,加入各种优化,但是最好情况只能通过88.78%的样例,无奈只能去看大佬的AC代码。
AC代码思路:
用dfs的思路去写折半搜索,加入c++中unique去重函数就顺利过了,要是不去重同样只能通过88.78%的样例,这个时候
你可能会想到wrong代码是不是没去重,其实去重了,而且手写去重代码以及unique都试过了,依然wrong了,由于我的存
在使得此题的通过率大幅度下降……具体的dfs写折半搜索在代码中,简单易懂
wrong代码:
1 #include <set> 2 #include <cstdio> 3 #include <algorithm> 4 #define LL long long 5 using namespace std; 6 7 LL w,A[10000005], B[10000005]; 8 int n, arr[50]; 9 10 bool cmp(int a,int b){ 11 return a>b; 12 } 13 14 inline LL find(LL k, int pos) { 15 int l = 1, r = pos; 16 int mid; 17 while (l < r) { 18 mid = (l + r + 1) >> 1; 19 if (A[mid] <= k) { 20 l = mid; 21 } 22 else { 23 r = mid - 1; 24 } 25 } 26 return A[l]; 27 } 28 29 int main(void) 30 { 31 scanf("%lld%d", &w, &n); 32 for (int i = 0; i < n; i++) 33 scanf("%d", &arr[i]); 34 sort(arr,arr+n,cmp); 35 36 LL res; 37 int m = n / 2+2, num = 0,flag; 38 for (int i = 0; i < (1 << m); ++i) { 39 flag = res = 0; 40 for (int j = 0; j < m; ++j) { 41 if (i & (1 << j)) 42 res += arr[j]; 43 if(res>w){ 44 flag=1; 45 break; 46 } 47 } 48 if(!flag) 49 //s.insert(res); 50 A[++num]=res; 51 } 52 sort(A+1,A+1+num); 53 /*int p=2,nidx=2; 54 while(p<=num){ 55 if(A[p]!=A[nidx-1]){ 56 A[nidx]=A[p]; 57 nidx++; 58 p++; 59 } 60 else 61 p++; 62 } 63 num=nidx-1; 64 */ 65 unique(A+1,A+1+num); 66 67 int cnt = 0, r = n - m; 68 for (int i = 0; i < (1 << r); ++i) { 69 flag = res = 0; 70 for (int j = 0; j < r; ++j) { 71 if (i & (1 << j)) 72 res += arr[m + j]; 73 if(res>w){ 74 flag=1; 75 break; 76 } 77 } 78 if(!flag) 79 B[++cnt]=res; 80 } 81 //sort(B+1,B+1+cnt); 82 83 /*p=2,nidx=2; 84 while(p<=cnt){ 85 if(B[p]!=B[nidx-1]){ 86 B[nidx]=B[p]; 87 nidx++; 88 p++; 89 } 90 else 91 p++; 92 } 93 cnt=nidx-1;*/ 94 //unique(B+1,B+1+cnt); 95 96 LL ans = 0; 97 for (int i = 1; i <= cnt; ++i) { 98 if (B[i] == w) { 99 ans = w; 100 break; 101 } 102 else if (A[2] > w - B[i]) { 103 ans = max(ans, B[i]); 104 } 105 else { 106 ans = max(ans, B[i] + find(w - B[i], num)); 107 } 108 } 109 printf("%lld\n", ans); 110 111 return 0; 112 }
其中注释掉的是手写去重代码,以及用set、map各种去重,甚至时间更长
AC代码:
1 #include <cstdio> 2 #include <algorithm> 3 #define LL long long 4 using namespace std; 5 6 LL acnt,w; 7 int n,deep; 8 LL res,arr[50],A[10000005]; 9 10 bool cmp(LL a,LL b){ 11 return a>b; 12 } 13 14 void dfs1(int nd,LL sum){ 15 if(nd==deep+1){ 16 A[++acnt]=sum; 17 return; 18 } 19 dfs1(nd+1,sum); 20 if(sum+arr[nd]<=w) 21 dfs1(nd+1,sum+arr[nd]); 22 } 23 24 void cal(LL x){ 25 LL k=w-x; 26 int l=1,r=acnt,mid; 27 while(l<r){ 28 mid=(l+r+1)>>1; 29 if(k>=A[mid]) 30 l=mid; 31 else 32 r=mid-1; 33 } 34 if(x+A[l]<=w) 35 res=max(res,x+A[l]); 36 } 37 38 void dfs2(int nd,LL sum){ 39 if(nd==n+1){ 40 cal(sum); 41 return ; 42 } 43 dfs2(nd+1,sum); 44 if(sum+arr[nd]<=w) 45 dfs2(nd+1,sum+arr[nd]); 46 } 47 48 int main(void) 49 { 50 scanf("%lld%d",&w,&n); 51 for(int i=1;i<=n;++i) 52 scanf("%lld",&arr[i]); 53 sort(arr+1,arr+1+n,cmp); 54 55 deep=n/2+2; 56 dfs1(1,0); 57 sort(A+1,A+1+acnt); 58 acnt=unique(A+1,A+acnt+1)-(A+1); 59 dfs2(deep+1,0); 60 printf("%lld\n",res); 61 62 return 0; 63 }
其中查找比w-sum小的最大的数用二分查找节约时间