这题自己想了两天~~~各种超时...还是只能去拜读好些多大牛的文章..才搞定这题..这题真的很好很典型很经典阿..用到的主体思想是DFSID..为辅的是二分答案~~
所谓DFSID既是迭代加深搜索...名字挺玄乎~~实质上就是先确定一个搜索的层数..然后用DFS搜完整棵树..如果没找到解..那么层数再放深一些..再次DFS搜整棵树..直道搜出解为止..很类似BFS了..但是是用DFS来实现的..空间节约了N多..时间上也没有慢多少..
而这道题..就是枚举答案..也就是输出的那个能切出来的rail最大个数...稍微想下,若最大个数为K..那么肯定是前K小的rail...这样更加方便来枚举答案..若K这个解切不出..那么看K/2..若切得出至少一种方案看K*2..然后不断夹逼得出答案...也就是二分的枚举答案..确定一个数后..就以不超过这个数层数来DFS找有没有至少一个解~~
光是这样还不够..DFS时要剪枝~~NOCOW给的剪枝确实非常之强劲阿...
- 很容易就能注意到,由于每块rail的价值是相等的——也就是说切小的要比切大的来的划算。那么我们在搜索能否切出i个rail的方案是自然要选最小的i个rail来切。 ----这似乎是我唯一自己能想到的..
- 经过一些实验可以发现,先切大的rail比先切小的rail更容易提前出解。同样,[先切小的board比先切大的board更容易提前出解?]{注:好像先切大的board要比先切小的更快}。{*我的程序先切小再切大第5个点就TLE了,而先切大再切小就快很多,见C++程序.{跟我一样,握个手,一定要先大后小!!!}} ----这个感觉说不出个所以然..有谁能证明下吗?我的程序board先大到小更快
- 由于r最大可能是1023,但是rail长度的范围却只有0~128,这点提醒了我们有很多rail的长度会是相同的。所以我们要避免冗余,优化搜索顺序。若有rail[i+1]=rail[i],则rail[i+1]对应的board一定大于等于rail[i]对应的board。可以通过这种方法剪掉很多冗余的枝条。--这个应该能想到才对
- 相应的,如果board[i]=board[i+1],那么从board[i]切下的最大的rail一定大于等于从board[i+1]切下的最大的rail。--我的程序没用到
- 对于切剩下的board(无法再切下rail),统计一下总和。如果这个值大于board长度的总和减去rail长度的总和,一定无解,可以剪枝。这个剪枝最关键。 ---- 最最牛屎彪悍的剪枝~~这个真的是很巧妙...但仔细想也应该能够想到..这里似乎没说得太明白..我是看了其他人的博客..所谓剩下的board就是将所有剩余木板长度小于最小rail长度的这些剩余长度加起来..board长度总合就是1~N个木板长度的总合..rail长度总和是当前DFSID时确定个数K的前K小的rail长度总和..
- 二分答案 -- 必须的..一个一个的枚举过去铁超时..
- 其实在读入的过程中,如果rail[i] > max{board} 那么这个rail应该舍去 By Clarkok
Orz了..总之这道题是学习了!!!!
Program:
/* ID: zzyzzy12 LANG: C++ TASK: fence8 */ #include<iostream> #include<istream> #include<stdio.h> #include<string.h> #include<math.h> #include<stack> #include<map> #include<algorithm> #include<queue> #define oo 2000000000 #define ll long long #define pi (atan(2)+atan(0.5))*2 using namespace std; int s[55],a[1030],N,R,have[55],mystack[1030],sums; bool ok(int R) { int i,k=1,dep=0,sum=sums,m=0; for (i=1;i<=N;i++) have[i]=s[i]; for (i=1;i<=R;i++) sum-=a[i]; while (1) { for (i=k;i<=N;i++) if (have[i]>=a[R-dep]) break; if (i==N+1) { if (!dep) return false; k=mystack[dep]; dep--; if (have[k]<a[1]) m-=have[k]; have[k]+=a[R-dep]; k++; }else { have[i]-=a[R-dep]; if (have[i]<a[1]) m+=have[i]; mystack[++dep]=i; if (dep==R) return true; if (m>sum) { dep--; if (have[i]<a[1]) m-=have[i]; have[i]+=a[R-dep]; k=i+1; continue; } if (a[R-dep]==a[R-dep-1]) k=i; else k=1; } } return false; } void getanswer(int R) { int l,r=1,mid,w,i; while (r<=R && ok(r)) r*=2; l=r/2; if (r>R) r=R+1; while (r-l>1) { mid=(l+r)/2; if (ok(mid)) l=mid; else r=mid; } printf("%d\n",l); return; } bool cmp(int a,int b) { return a>b; } int main() { freopen("fence8.in","r",stdin); freopen("fence8.out","w",stdout); int i; sums=0; scanf("%d",&N); for (i=1;i<=N;i++) { scanf("%d",&s[i]); sums+=s[i]; } sort(s+1,s+1+N,cmp); scanf("%d",&R); for (i=1;i<=R;i++) scanf("%d",&a[i]); sort(a+1,a+1+R); while (R>=0 && a[R]>s[1]) R--; while (N>=0 && s[N]<a[1]) N--; mystack[0]=0; a[0]=0; getanswer(R); return 0; }