常用技巧(一)2
来源:互联网 发布:福建万照软件 编辑:程序博客网 时间:2024/06/07 23:33
3.2.3弹性碰撞
Physics Experiment
用N个半径为R厘米的球进行了如下实验
在H米高的位置设置了一个圆筒,将球垂直放入(从下向上数第i个球的底端距离地面高度为H+2R)。实验开始时最下面的球开始掉落,此后每一秒又有一个球开始掉落。不计空气阻力,并假设球与球或地面间的碰撞是弹性碰撞
请求出实验开始后T秒钟时每个球底端的高度
首先考虑一下只有一个球的情形 。这时只是单纯的物理问题。从高为H的位置下落的话需要花费的时间是。。
接下来再考虑多个球的情形。乍一看,因为多个球之间会有碰撞,必须对物理运动进行模拟,事实上并没有这个必要。我们来回忆一下此前的题目。在那道题中两只蚂蚁相遇后并不是各自折返,而是擦身而过,于是问题简化了
这里的问题可以用同样方法思考。首先先来考虑一下R=0的情况。如果认为所有球都是一样的,就可以无视他们的碰撞,视为直接相互穿过继续运动。由于在有碰撞时球的顺序不会发生改变,所以忽略碰撞,将计算得到的坐标进行排序后,就能知道每个球的最终位置。那么R>0时要怎么办呢?这种情况下的处理方法基本相同,对于从下方开始的第i个球,在按照rR=0计算的结果上加上2Ri就好了
const double g=10.0;//重力加速度//输入int N,H,R,T;double y[MAX_N]; //球的最终位置//求出T时刻球的位置double calc(int T){ if(T<0) return H; double t=sqrt(2*H/g); int k=(int)(T/t); if(k%2==0) { double d=T-k*t; return H-g*d*d/2; } else { double d=k*t+t-T; return H-g*d*d/2; }}void solve(){ for(int i=0;i<N;i++) { y[i]=calc(T-i); } sort(y,y+N); for(int i=0;i<N;i++) { printf("%.2f%c",y[i]+2*R*i/100.0,i+1==N?'\n':' '); }}3.2.4折半枚举(双向搜索)
4 Value whose Sum is 0
给定各有n个整数的四个数列A,B,C,D。要从每个数列中各取出1个数,使四个数的和为0.求出这样的组合的个数。当一个数列中有多个相同的数字时,把它们作为不同的数字看待
枚举全部判断一遍不可行。不过将它们对半分成AB和CD再考虑,就可以快速解决了。从2个数列中选择的话只有n*n种组合,所以可以进行枚举,先从A,B中取出a,b后,为了使总和为0则需要从C,D中取出c+d=-a-b。因此先将从C,D中取数字的n*n种方法全都枚举出来,将这些和排好序,这样就可以运用二分搜索了,这个算法的复杂度是。。
有时候,问题的规模较大,无法枚举所有元素的组合,但能够枚举一半元素的组合。此时。若将问题拆成两半后分别枚举,再合并它们的结果这一方法往往非常有效
int n;int A[MAX_N],B[MAX_N],C[MAX_N];int CD[MAX_N*MAX_N]; //C和D中数字的组合方法void solve(){ //枚举从C和D中取出数字的所有方法 for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { CD[i*n+j]=C[i]+D[j]; } } sort(CD,CD+n*n); long long res=0; for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { int cd=-(A[i]+B[j]); //取出C,D中和为cd的部分 res+=upper_bound(CD,CD+n*n,cd)-lower_bound(CD,CD+n*n,cd); } } printf("%lld\n",res);}
超大背包问题有重量和价值分别为wi,vi的n个物品。从这些物品中挑选总重量不超过W的物品,求所有挑选方案中价值总和的最大值
这个问题是第二章介绍过得背包问题。不过这次价值和重量都可以是非常大的数值,相比之下n比较小,使用DP求解背包问题的复杂度是O(nW),因此不能用来解决这里的问题,此时我们应该利用n比较小的特点来寻找其他办法
挑选物品的方法总共有2^n中,所以不能直接枚举,但是像前面一样拆成两半之后再枚举的话,每个部分只有20个所以是可行的。利用拆成两半后的价值和重量,我们能求出原先的问题。我们把前半部分中的选取方法对应的重量和价值总和记为w1,v1.这样再后半部分寻找总重w2<=W-w1时使v2最大的选取方法就好了
因此,我们需要思考从枚举得到的(w2,v2)的集合中高效寻找max{v2|w2<=W'}的方法。首先,显然我们可以排除所有w2[i]<=w2[j]并且v2[i]>=v2[j]的j.这一点可以按照w2,v2的字典序排列后简单做到。此后剩余的元素都满足w2[i]<w2[j]<>v2[i]<v2[j].要计算max{v2|w2<=W'}的话,一次搜索需要O(logM)的时间
typedef long long ll;int n;ll w[MAX_N],v[MAX_N];ll W;pair<ll,ll>ps[1<<(MAX_N/2)]; //(重量,价值)void solve(){ //枚举前半部分 int n2=n/2; for(int i=0;i<1<<n2;i++) { ll sw=0,sv=0; for(int j=0;j<n2;j++) { if(i>>j&1) { sw+=w[i]; sv+=v[j]; } } ps[i]=make_pair(sw,sv); } //去除多余的元素 sort(ps,ps+(1<<n2)); int m=1; for(int i=1;i<1<<n2;i++) { if(ps[m-1].second<ps[i].second) { ps[m++]=ps[i]; } } //枚举后半部分并求解 ll res=0; for(int i=0;i<1<<(n-n2);i++) { ll sw=0,sv=0; for(int j=0;j<n-n2;j++) { if(i>>j&1) { sw+=w[n2+j]; sv+=v[n2+j]; } } if(sw<=W) { ll tv=(lower_bound(ps,ps+m,make_pair(W-sw,INF))-1)->second; res=max(res,sv+tv); } } printf("%lld\n",res);}0 0
- 常用技巧(一)2
- 表单常用技巧(一)
- javaScript 常用技巧(一)
- PB常用技巧(一)
- javaScript 常用技巧(一)
- [JAVA] 常用技巧(一)
- 3.2常用技巧(一)
- 常用技巧(一)3
- VC++ MFC 常用技巧 (一)
- .NET 中的常用小技巧(一)
- javascript常用经典技巧(一)
- VC++ MFC 常用技巧(一)
- (一)Android ViewTreeObserver的常用技巧
- VMware Workstation 常用技巧(一)
- 常用技巧(一)尺取法
- PS--常用操作技巧(一)快捷键
- 常用CSS技巧(一)
- C++常用技巧一
- 设计模式拾荒之迭代器模式 ( Iterator Pattern )
- 部署WAR包到TOMCAT的方法
- Spring提高篇(六):文件资源操作工具类
- 防止SQL注入的五种方法
- 部件MSCOMCTL.OCX或其附件之一不能正确注册:一个文件丢失或无效
- 常用技巧(一)2
- Monit:开源服务器监控工具
- OC 重点block-------5-------block作为函数的返回值
- Linux学习之十一egrep及扩展正则表达式(egrep)
- C++程序设计 重载
- opencv3中的imwrite函数详解
- 数据结构——线性表总结
- bzoj1110 [POI2007]砝码Odw 贪心+进制拆分
- HID Usage Tables (用途表)