usaco4.12Fence Rails(迭代加深)
为了这题还去学了下迭代加深 回来还是不会写
只好参考各大神的代码及题解了
二分枚举最大可以切的块数 然后就是各种分析及优化
USACO题解里写了7个优化。。
问题分析
抽象一下就可以发现,算法的本质是多重背包问题。 补充:这题与破锣乐队都是多个背包,不可重复放物品。区别在于破锣乐队要有顺序,此题不需要,这样此题就必须要搜索才行。 单个背包的问题我们可以用DP解决,但是对于这种问题我们只能用搜索了。 但是可以看一看这道题的数据规模:1<=n<=50,1<=r<=1023。如此大的规模我们只能考虑进一步的优化。
我采用的是dfsid搜索每一个rail来源的board。以下技巧都是针对这种搜索顺序来制定的。 (注:rail是所需要切成的东西,board是供应商提供的原料)
如果使用dancing links的话,可以让程序的常数快2倍。
优化技巧
- 很容易就能注意到,由于每块rail的价值是相等的——也就是说切小的要比切大的来的划算。那么我们在搜索能否切出i个rail的方案是自然要选最小的i个rail来切。
- 经过一些实验可以发现,先切大的rail比先切小的rail更容易提前出解。同样,[先切小的board比先切大的board更容易提前出解?]{注:好像先切大的board要比先切小的更快}。{*我的程序先切小再切大第5个点就TLE了,而先切大再切小就快很多,见C++程序.{跟我一样,握个手,一定要先大后小!!!}}
- 由于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长度的总和,一定无解,可以剪枝。这个剪枝最关键。
- 二分答案
- 其实在读入的过程中,如果rail[i] > max{board} 那么这个rail应该舍去 By Clarkok
-
1 /* 2 ID: shangca2 3 LANG: C++ 4 TASK: fence8 5 */ 6 #include <iostream> 7 #include<cstdio> 8 #include<cstring> 9 #include<algorithm> 10 #include<stdlib.h> 11 using namespace std; 12 int bo[55],te[55],ra[1050],v; 13 int n,m,res,s,sum[1050],flag; 14 void dfs(int tt,int st) 15 { 16 int i; 17 if(flag) return ; 18 if(tt==0)//全部可以切完 19 { 20 flag = 1; 21 return ; 22 } 23 int ss=0,o; 24 for(i = 1 ; i <= n ; i++)//当前木板剩余值已经大于最多的剩余值 肯定不用再搜 25 { 26 27 if(te[i]<ra[1]) 28 ss+=te[i]; 29 } 30 if(ss>v) return ; 31 for(i = st ; i <= n ; i++) 32 { 33 if(te[i]>=ra[tt]) 34 { 35 36 if(tt-1>=1&&ra[tt]==ra[tt-1])//一个小优化 如果ra[tt]已经在i~N里搜了 那么前面跟它相等的 就不会在i之前搜了 37 o = i; 38 else 39 o = 1; 40 te[i]-=ra[tt]; 41 dfs(tt-1,o); 42 te[i]+=ra[tt]; 43 } 44 } 45 return ; 46 } 47 int main() 48 { 49 //freopen("fence8.in","r",stdin); 50 //freopen("fence8.out","w",stdout); 51 int i; 52 cin>>n; 53 for(i = 1 ; i <= n ; i++) 54 { 55 scanf("%d",&bo[i]); 56 s+=bo[i]; 57 } 58 sort(bo+1,bo+n+1); 59 for(i = 1; i <= n ; i++) 60 te[i] = bo[i]; 61 scanf("%d",&m); 62 for(i = 1; i <= m ; i++) 63 { 64 scanf("%d",&ra[i]); 65 66 } 67 sort(ra+1,ra+m+1); 68 for(i = 1; i <= m ; i++) 69 sum[i] = sum[i-1]+ra[i]; 70 int low=0,high = m; 71 while(low<high)//二分找一下 满足最多可以切多少块 72 { 73 int mm = (low+high+1)>>1; 74 v = s-sum[mm]; 75 flag = 0; 76 dfs(mm,1); 77 if(flag) 78 low = mm; 79 else 80 high = mm-1; 81 } 82 printf("%d\n",low); 83 return 0; 84 }