双向搜索

双向搜索--从初态和终态出发各搜索一般状态,产生两棵深度减半的搜索树,在中间交会、组合成最终的答案

例题:送礼物(牛客网)

题目描述:

  作为惩罚,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小的最大的数用二分查找节约时间

猜你喜欢

转载自www.cnblogs.com/gn1314/p/11484819.html