分治与二分三分用法介绍
来源:互联网 发布:梦幻西游宠物数据 编辑:程序博客网 时间:2024/05/21 21:48
今天主要是写了一些分治的题目主要是一般的分治、二分、三分、大概了解了在树上点分治的思想(代码就gg了)…… 做了有5道这方面的题目 今天所理解的分治就是不断想办法将问题规模变小而问题的性质不变 就可以通过这种方法递归
question 1 : poj3714 Raid;
题意 : 给你2n个点
前n个点一组 后n个点一组
n 是 1e5
问你前一组和后一组中任意两个点的最小距离
solve : 考虑一个问题给你n个点 问你任何两个点之间的最小距离
我们可以用分治的思想去解决这个问题
把n个点分成 n / 2 的两个部分
递归求出左边n / 2个点的任何两点的最小距离 和 右边n / 2 点的最小距离
然后用d 去记录两者的最小值 记录之后 从中间到两边 分别 划 d 的距离 不难发现所有距离可能小于d的点对都必须在这个区间之内。
然后对于左边的一个点 右边的可能与它距离小于d的点都在 纵坐标 y - d 到 y + d 之间 ;不难证明对于一个左边的点最多有6个右边的点与之对应使得 两点之间的距离可能小于d,有了这个结论就可以先将y排序然后暴力写出了
知道了上面那个题的做法 只需要再判断这两个点是不是位于左右两边就可以了。
AC code :
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const double oo=1e50;
int n;
double ab(double x)
{
return x>0?x:-x;
}
struct node
{
double x,y;
bool bel;
}a[200010],temp[200010];
bool cx(const node &a,const node &b)
{
return a.x<b.x;
}
bool cy(const node &a,const node &b)
{
return a.y<b.y;
}
double min(double x,double y)
{
return x<y?x:y;
}
double dis(node p,node q)
{
if (p.bel==q.bel)return oo;
return sqrt((p.x-q.x)*(p.x-q.x)+(p.y-q.y)*(p.y-q.y));
}
double make(int l,int r)
{
if (l==r)return oo;
int m=(l+r)/2,i,j,k,ll,rr,p,q,x,y,z,cnt=0;
double ans=min(make(l,m),make(m+1,r));
for (i=l;i<=r;i++)
if (ab(a[i].x-a[m].x)<=ans)
temp[++cnt]=a[i];
sort(temp+1,temp+cnt+1,cy);
for (i=1;i<=cnt;i++)
for (j=i+1;j<=cnt;j++)
{
if (temp[j].y-temp[i].y>ans)break;
ans=min(ans,dis(temp[i],temp[j]));
}
return ans;
}
int main()
{
int i,j,k,T;
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
for (i=1;i<=n;i++)
{
scanf("%lf%lf",&a[i].x,&a[i].y);
a[i].bel=0;
}
for (i=1;i<=n;i++)
{
scanf("%lf%lf",&a[i+n].x,&a[i+n].y);
a[i+n].bel=1;
}
sort(a+1,a+2*n+1,cx);
printf("%.3f\n",make(1,2*n));
}
}
question 2 : codeforce 768 B
题意:给一个数n,和一个区间[l,r] (r-l<1e5,n<2^50),每次可以把数n分成(n/2,n%2,n/2)知道所有数变成0或1,问区间内有多少个1?
solve : 不难发现 要询问区间长度最多为 1e5 不难想到去询问区间内每一个数是 1 还是 0 然后就通过递归n去判断区间的每一个 在l 到 r之间的1到底是多少 不难发现这棵树是一棵类似于完全二叉树的东西,递归层数不会超过50 然后就可以轻松愉快的ac 了 感觉像线段树。
AC code :
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
long long l,r;
long long cnt =0;
int ans = 0;
long long len =1;
int dfs(longlong n,longlong ll,longlong rr){
if(n ==0 || ll > r || rr < l){
return0;
}
if(n ==1 && ll == rr){
return1;
}
long long mid = (ll + rr) / 2;
return dfs(n %2,mid,mid) + dfs(n / 2,ll,mid -1) + dfs(n / 2,mid +1,rr);
}
int main(){
ios_base :: sync_with_stdio(false);
long long n;
cin >> n >> l >> r;
long long m = n;
while(m >1){
len *= 2;
len ++;
m /= 2;
}
ans = dfs(n,1,len);
cout << ans << endl;
return0;
}
question 3: codeforce 448C
有n个木板 高度分别为h[i] 现在给木板刷漆可以横着刷也可以竖着刷一次只能横着刷一排(不可以跳过)或者竖着刷一个木板 问你最少刷多少次可以将所有木板刷完。
solve : 不难发现我们在横着刷的时候一定要刷完最小高度的一个才可以停止否则肯定会使得最后的结果增大,除了横着刷我们还可以竖着刷,如果竖着刷就一次性全部刷完才可能会达到最优的情况。所以现在就得到两种刷的方案 显然第二种方案需要n次才可以全部刷完,而对于第一种当刷完最短的那个木板之后我们就可以把这个木板的位置作为分开的地方将问题划分成左边部分和右边部分进行递归分治。
AC code :
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn =5005;
const int INF =1e9 + 7;
int h[maxn] = {0};
int merge(int l,int r){
if(l > r){
return0;
}
if(l == r){
if(h[l]){
// cout << l << ' ' << r << endl;
return1;
}
return0;
}
int ans = r - l +1;
int sum =0;
int mi = INF;
for(int i = l ;i <= r;++i){
mi = min(h[i],mi);
}
sum = mi;
int ll = l;
// cout << sum << endl;
for(int i = l;i <= r;++i){
h[i] -= mi;
if(h[i] ==0){
sum += merge(ll,i-1);
ll = i + 1;
}
}
sum += merge(ll,r);
// cout << l << ' ' << r << ' ' << sum << endl;
ans = min(ans,sum);
return ans ;
}
int main(){
ios_base :: sync_with_stdio(false);
int n;
cin >> n;
for(int i =1;i <= n;++i){
cin >> h[i];
}
int ans = merge(1,n);
ans = min(ans,n);
cout << ans << endl;
return0;
}
question 4 NEERC J Buoys
题意 : 一维坐标给你n个点 让你移动任何多个点 问你使得任意两个点之间的距离相等的最小移动距离 n <= 400
solve : 首先我就想去二分答案 但是我发现我不会写check (太菜了,要是汀老师写他肯定会)没办法我就只能去三分距离了 三分距离 然后暴力枚举那一个点不动计算其他点的移动距离求一个最小的就可以了 幸好这个题对精度要求不严格,(PS : NEERC的题目竟然是文件输入输出……)
AC code :
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn =410;
const double INF =1e12;
const double eps =1e-12;
double ab(double x) {return x >0.0 ? x : -x;}
double a[maxn];
double b[maxn];
double c[maxn];
double ans = INF;
int n;
double check(double d){
double temp =0;
double s = INF;
for(int i =0;i < n;++i){
temp = 0;
for(int j =0;j < n;++j){
c[j] = a[i] + (j - i) * d;
temp += ab(c[j] - a[j]);
}
s = min(s,temp);
if(temp < ans){
for(int k =0;k < n;++k){
b[k] = c[k];
ans = min(ans,s);
}
}
}
return s;
}
int main(){
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d",&n);
for(int i =0;i < n;++i){
scanf("%lf",&a[i]);
}
double l =0;
double r = INF;
while(r - l > eps){
double mid = (2 * l + r) /3;
double midd = (l +2 * r) / 3;
if(check(mid) > check(midd)){
l = mid;
}
else{
r = midd;
}
}
printf("%.4f\n",ans);
for(int i =0;i < n;++i){
printf("%.10f ",b[i]);
}
return0;
}
question 5: codeforce 578 C
一道三分题 挺水的难点在于卡你精度 …..
AC CODE :
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <set>
using namespace std;
const int maxn=200000+10;
const double INF =1<<30;
double eps=1e-11;
set <int> st;
int cnt = 0;
double x;
int n;
typedef longlong ll;
double a[maxn];
double b[maxn];
double _abs(double x){
if(x<0)return -x;
else return x;
}
double F(double x){
for(int i=1;i<=n;i++){
b[i]=a[i]-x;
}
double ans1=b[1],ans2=b[1];
double _max=ans1,_min=ans2;
for(int i=2;i<=n;i++){
if(ans1>0){
ans1+=b[i];
}else{
ans1=b[i];
}
_max=max(_max,ans1);
if(ans2<0){
ans2+=b[i];
}else{
ans2=b[i];
}
_min=min(_min,ans2);
}
return max(_abs(_min),_abs(_max));
}
int main(void){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf",&a[i]);
if(st.count(a[i])){
cnt ++;
}
else
st.insert(a[i]);
}
if(cnt == n -1 && a[1] == a[2]){
cout << 0 << endl;
return0;
}
double L=-INF,R=INF;
eps /= 2;
while(R-L>eps){
double m1=L+(R-L)/3;
double m2=R-(R-L)/3;
if(F(m1)<F(m2)){
R=m2;
}else{
L=m1;
}
//
}
printf("%.9lf",F(L));
return0;
}
- 分治与二分三分用法介绍
- 分治搜索(二分,三分)
- 二分,三分 分治求 a^n
- 二分与三分
- 二分与分治
- 二分搜索与三分搜索的应用
- 二分和三分总结与误区分析
- 二分&三分
- 递归与分治:二分查找
- 【分治法】分治法与二分搜索,棋盘覆盖问题
- 与二分分治有关的几个函数
- 【算法·递归与分治】二分查找
- 二分思想与分治法、排序思想
- 递归与分治——二分查找
- CDQ分治与整体二分小结
- 三分+二分求极值点或者与x轴交点
- hdu2298 Toxophily 三分+二分
- hdu2298 三分+二分查找
- 勇于尝试——AJ
- JPA之第一个JPA程序
- [BZOJ1565][NOI2009]植物大战僵尸-拓扑排序-网络流
- 程序员是怎样撩到一个女朋友的?
- 区分ADC0804模拟地与数字地消除干扰的方法
- 分治与二分三分用法介绍
- HDU
- Linux 安装oracle11g 出现的问题及解决方案
- 什么是浮地技术
- Oracle中行转列、列转行的使用
- 玲珑杯Round20
- Struts2学习笔记——Struts2与Spring整合
- hibernate使用异常汇总
- apache kafka技术分享系列(目录索引)