您的位置:首页 > 其它

常用技巧(一)2

2017-05-09 15:56 106 查看
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);
}

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: