不知道为什么很多人拒绝这题打搜索。。。其实搜索在充分剪枝后时间是非常优秀的,不管数据怎样基本都可跑出
首先一个显然结论:对于某种状态,他抓到的小精灵一定是一个连续的区间。
因此我们可以枚举这个区间的左右端点,进行搜索,但是这样显然会T,我们考虑剪枝:
1.可行性剪枝,当前时间大于最大时间直接return(废话)
2.最优性剪枝,对于每种状态,我们用两个数组来记录出现这种状态所需要的最短时间,以及最短时间下的最优答案,若时间长答案还小,直接return
3.确定枚举方向,如果上一步从左向右走,那么当前必然向左走。很显然,若继续向右走,与上一次枚举的有重复。
4.一个比较玄学但是非常有用的剪枝:对于当前左右端点l,r,在枚举右端点时,从m处开始枚举到r+1,而不是从r+1枚举到m,左端点同理。看上去比较玄学,实际上是有一定原理的:若我们枚举到的端点离当前端点很近,那么会重复走很多位置,在大多数情况下浪费时间,往往无法得到最优解,而较远端点更可能获得更优的答案,结合剪枝2,会将较近端点剪掉,除去很多不必要搜索。
综合以上,我们就可以得到一个时间碾压正解的搜索(欢迎造数据hack)
附代码:
1 #include<bits/stdc++.h> 2 #define cri const register int 3 using namespace std; 4 int n,k,m,maxt,ans,ys[2005],yx[2005],f[105][105],ff[105][105]; 5 char v[105]; 6 inline int abs(int x){return x<0?-x:x;} 7 struct jing{ 8 int a,b,t; 9 friend bool operator < (jing a,jing b){ 10 return a.a<b.a; 11 } 12 }c[105]; 13 void dfs(cri ti,cri l,cri r,cri na,cri o){ 14 if(ti<maxt)ans=max(ans,na); 15 if(ti>=maxt)return; 16 if(ti>=f[l][r]&&na<=ff[l][r]){ 17 return; 18 } 19 if(f[l][r]>ti)f[l][r]=ti,ff[l][r]=na; 20 if(o!=0){ 21 int sum=0; 22 for(int i=m+1;i>=r+1;i--) 23 if(ti+c[i].a-c[l].a<=c[i].t) 24 sum+=c[i].b; 25 for(int i=m+1;i>=r+1;i--){ 26 if(ti+c[i].a-c[l].a<=c[i].t){ 27 dfs(ti+c[i].a-c[l].a,l,i,na+sum,0); 28 sum-=c[i].b; 29 } 30 } 31 } 32 if(o!=1){ 33 int sum=0; 34 for(int i=l-1;i>=1;i--){ 35 if(ti+c[r].a-c[i].a<=c[i].t){ 36 sum+=c[i].b; 37 } 38 } 39 for(int i=1;i<=l-1;i++){ 40 if(ti+c[r].a-c[i].a<=c[i].t){ 41 dfs(ti+c[r].a-c[i].a,i,r,na+sum,1); 42 sum-=c[i].b; 43 } 44 } 45 } 46 } 47 inline int read(){ 48 int x=0; 49 char ch=getchar(); 50 while(ch<'0'||ch>'9')ch=getchar(); 51 while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-48,ch=getchar(); 52 return x; 53 } 54 int main(){ 55 memset(f,0x3f,sizeof(f)); 56 scanf("%d%d%d",&n,&k,&m); 57 for(int i=1;i<=m;i++) 58 c[i].a=read(),c[i].b=read(),c[i].t=read(),maxt=max(maxt,c[i].t); 59 c[m+1].a=k;c[m+1].b=0,c[m+1].t=0; 60 maxt++; 61 sort(c+1,c+m+2); 62 for(int i=1;i<=m+1;i++) 63 ys[c[i].a]+=c[i].b,yx[c[i].a]=i; 64 dfs(1,yx[k],yx[k],ys[k],-1); 65 printf("%d",ans); 66 return 0; 67 }