【总结】搜索的剪枝二分预处理和离散化等优化

来源:互联网 发布:爱苹果软件 编辑:程序博客网 时间:2024/06/10 07:35

送给圣诞夜的贺卡

位运算加速,因为集合的总元素个数较少,可以用位运算加速。

降序排序,使得越到后面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");elseprintf("%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;}elsel = 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");elseprintf("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 ++;elsej ++;}}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;}