分治专题小结

本文介绍了四次算法机考的主要题型,包括快速幂运算、二维坐标系中最短距离计算、逆序对数量求解、动态变化数组中位数查询以及大整数乘法的实现。通过实例代码详细解析了解题思路和关键步骤,适合备考和学习算法的读者参考。
摘要由CSDN通过智能技术生成

(4.22算法机考,慌得一批啥也不会来写点东西
认真准备三次机考考了第7、8、6名,比上不足比下有余。。。
1、入门——快速幂
题目:给定整数n、c、m,求(n的c次方)mod m
思路:
1)当c为偶数时,nc= nc/2 * nc/2
2)当c为奇数时,nc = nc/2 * nc/2 * n
而(a*b) mod m = (a mod m) * (b mod m)
则代码如下:

#include <iostream>
using namespace std;
typedef long long ll;
ll mi(ll a,ll x,ll m){//a的x次方 mod m
    if (x==0) return 1;
    else if (x==1) return a%m;
    else{
        ll res=1;
        if (x%2)  return (mi(a,x/2,m)%m)*(mi(a,x/2+1,m)%m); //奇数
        return (mi(a,x/2,m)%m)*(mi(a,x/2,m)%m); //偶数
    }
}
int main(){
    ll a,b,c;
    while (cin>>a>>b>>c){
        if (a==0 && b==0 && c==0) break;
        else cout<<mi(a,b,c)%c<<endl;
    }
}

2、经典【最近点对】
题目:二维坐标系中给定一组点,输出点集中两点间的最短距离
输入:第一行n代表点的个数
接下来n行,每行是一个点的x y 坐标(不超过double范围)
输出:最短距离(保留两位小数)
思路
暴力 (骗分用,时间复杂度O(n2),数据给个1000左右就炸了)
将点集按x坐标排序,取中位数分成两个集合
分类讨论:
1)将俩集合递归,各自求最短距离,并比较得出更短距离记为mind
2)最短距离构成的两点可能分别位于两个集合中,所以分别从两个集合中每次各选一点求最短距离
!!!如果两个集合都是从头遍历到尾,跟暴力又有什么区别呢?所以我们要对两个集合中的点进行筛选。横坐标 距离中心点超过mind则筛去,再以y排序,纵坐标 之间不超过mind则计算距离。
由此比较得出最短距离。

#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cmath>
#define MAX 999999999
#define maxn 100001
using namespace std;

struct node{double x,y;};
node p[maxn];
bool cmp(node a, node b){ //按x升序排列
	if (a.x!=b.x) return a.x<b.x;
	return a.y>b.y;
}
double dis(int i,int j){  //计算两点之间的距离
    double t1=p[i].x-p[j].x,t2=p[i].y-p[j].y;
    return sqrt(t1*t1+t2*t2);
}
double closest(int left,int right){ //分治法计算最近点对
	if (left==right) return MAX;    //1
	if (right-left==1) return dis(left,right); //2
	//>=3
	double mini=MAX,disl,disr,ttt;
	int i=0,j=0,k=0,mid;
	mid=(left+right)/2;
	disl=closest(left,mid);
	disr=closest(mid+1,right);
	mini=disl<disr?disl:disr;

	//用x找右-左<=mini的点
	int t[maxn]={0};
    for (i=left;i<=right;i++){
		if (abs(p[mid].x-p[i].x)<=mini) t[k++]=i;//储存符合的序号
	}
	//在这些点中找y上-下<=mini的点,得出最短距离
	for (i=0;i<k;i++){
		for (j=i+1;j<k && j<i+7;j++)
			if (abs(p[t[j]].y-p[t[i]].y)<mini){
			    ttt=dis(t[i],t[j]);
                if (ttt<mini) mini=ttt;
			}
	}
	return mini;
}

int main(){
	int n; cin>>n;
    for (int i=0;i<n;i++) cin>>p[i].x>>p[i].y;
    sort(p,p+n,cmp);
	cout<<fixed<<setprecision(2)<<closest(0,n-1);
	return 0;
}

3、一个数组,计算有多少个逆序对
思路:快速排序时顺便可以计数,则只需要O(nlogn)时间
关键代码片段:

ll n[1000001],cnt=0;
void Merge(ll a,ll mid,ll b){
    if (b-a>0){ //中间有数
		ll i=a,j=mid+1,k=0;
        ll result[b-a+1];
        while (i<=mid && j<=b) {
            if (n[i]<=n[j]) result[k++]=n[i++];  //正序对
            else{    //逆序对
                cnt+=mid-i+1;
                result[k++]=n[j++];
            }
        }
        while (j<=b) result[k++]=n[j++];
        while (i<=mid) result[k++]=n[i++];
        for (k=0;k<b-a+1;k++) n[a+k]=result[k];
    }
}
void mergeSort(ll a,ll b) {
    if (a<b) {
        ll mid=(a+b)/2;
        mergeSort(a,mid);
        mergeSort(mid+1,b);
        Merge(a,mid,b);
    }
}
int main(){
	ll N; cin>>N;
	for (ll i=0;i<N;i++) cin>>n[i];
	mergeSort(0,N-1);
	cout<<cnt;
	return 0;
}

4、求两个变化数组的中位数
题目:给定两组数,对任意一组±同一个数,求整体的中位数。
思路:分别求俩数组的中位数,然后逼到1/2个。
关键代码片段:

ll a[100001],b[100001];
ll mid(ll n,ll m,ll add){
    ll maxl,minr,left=0,right=n;
    while (left<=right){
        ll i=(right+left)/2,j=(m+n+1)/2-i;
        if (i<n && a[i]+add<b[j-1]) left=i+1; //i小了
        else if (i>0 && j<m && a[i-1]+add>b[j]) right=i-1; //i大了
        else{
            if (i==0) maxl=b[j-1];//a[0]都不行
            else if (j==0||j>=m) maxl=a[i-1]+add; //b[0]都不行或者越界
            else{//非特殊情况,找两个值的最大值
                if (a[i-1]+add>b[j-1]) maxl=a[i-1]+add; 
                else maxl=b[j-1];
            }
            if ((n+m)%2) return 2*maxl; //奇数个数求中位数
            if (i==n) minr=b[j]; //a[n-1]都不行
            else if (j==m) minr=a[i]+add; //b[m-1]都不行
            else{//非特殊情况,找两个值的最小值
                if (a[i]+add<b[j]) minr=a[i]+add;
                else minr=b[j];
            }
            return minr+maxl;//偶数个数求中位数
        }
    }
}
int main(){
    ll n,m,q,x,y,t;
    cin>>n>>m>>q;
    //让a短
    if (n<=m){
        for (ll i=0;i<n;i++) cin>>a[i];
        for (ll j=0;j<m;j++) cin>>b[j];
    }
    else{
        for (ll i=0;i<n;i++) cin>>b[i];
        for (ll j=0;j<m;j++) cin>>a[j];
    }
    //开始查询
    for (ll k=0;k<q;k++){
        cin>>x>>y;
        if (x==1){ //对a操作
            if (n<=m) t=mid(n,m,y); //对a组加
            else t=mid(m,n,-y)+2*y; //b组加
        }
        else{ //对b操作
            if (n<=m) t=mid(n,m,-y)+2*y; //b加
            else t=mid(m,n,y);
        }
        if (t%2) cout<<t/2<<".5"<<endl; //要不整数要不.5
        else cout<<t/2<<endl;
    }
    return 0;
}

5、汉诺塔问题
题目:ABC 3根柱,A上有n个从小到大排列的圆盘,求全部盘到C柱从小到大排列的移动次数。
思路:把n-1个移动到B,最后1个移到C,再把B的n-1个移到C。

#include <stdio.h>
#include <stdlib.h>
static int count = -1;

void hanoi(int n,char one,char two,char three){
  	if(n==1) move(one,three);
  	else{
      	hanoi(n-1,one,three,two);  //首先把n-1个从one移动到two
      	move(one,three); //然后把最后一个n从one移动到three
      	hanoi(n-1,two,one,three);  //最后再把n-1个从two移动到three
  	}
}
void move(char x,char y)    {
	count++;
 	if( !(count%5) ) printf("\n");
 	printf("%c移动至%c  ",x,y);
}
int main(){          
 	int m;
 	printf("请输入一共有多少个板子需要移动:");
 	scanf("%d",&m);
 	printf("以下是%d个板子的移动方案:\n",m);
 	hanoi(m,'A','B','C');
 	return 0;
}

6、大整数乘法
关键代码片段:

#include <cmath>
int SIGN(long A){ return A > 0 ? 1 : -1;} //正负
long bigIntMultiply_Nonideal(long X, long Y, int n_x, int n_y){
    if (X == 0 || Y == 0) return 0;
    else if ((n_x == 1 && n_y == 1)|| n_x==1||n_y==1) return X * Y;
    else {
        int n_x0 = n_x / 2,n_x1 = n_x - n_x0;
        int n_y0 = n_y / 2, n_y1 = n_y - n_y0;

        long a=(long)(X/pow(10,n_x0)),b=(long)(X%(long)pow(10, n_x0));
        long c=(long)(Y/pow(10,n_y0)),d=(long)(Y%(long)pow(10,n_y0));
        long E = bigIntMultiply_Nonideal(a, c, n_x1, n_y1);
        long F = bigIntMultiply_Nonideal(b, d, n_x0, n_y0);
        long G = bigIntMultiply_Nonideal((long)(a * pow(10, n_x0) - b), (long)(d - c * pow(10, n_y0)), n_x1, n_y1);
        return (long)(E * pow(10, (n_x0 + n_y0)) + (G + E * pow(10, (n_x0 + n_y0)) + F) + F);
    }
}
int main(){
    long X = 0,Y = 0;
    int n_x = 0,n_y = 0;
    cin >> X>> Y;
    cin >> n_x>> n_y; //长度
    cout << "\nX*Y=" << bigIntMultiply_Nonideal(X, Y, n_x, n_y) << endl;
    return 0;
}

7、喜串
题目:a 和 b 相同。将 a 分成 a1 与 a2 两个等长串,b 分成 b1 与 b2 两个等长串,其子串需满足以下两个条件之一:a1 与 b1 互为喜串且 a2 与 b2 互为喜串;a1 与 b2 互为喜串且 a2 与 b1 互为喜串。
代码:

#include <iostream>
using namespace std;
string x,y;
bool ishappy(int a,int b,int len){
    int i;
    for (i=0;i<len;i++){
        if (x[a+i]!=y[b+i]) break;
    }
    if (i==len) return true;
    if (len%2 || !len) return false;
    len/=2;
    if (ishappy(a,b+len,len) && ishappy(a+len,b,len)) return true;
    if (ishappy(a,b,len) && ishappy(a+len,b+len,len)) return true;
    return false;
}
int main(){
    cin>>x>>y;
    if (x.size()!=y.size()) cout<<"No";
    if (ishappy(0,0,x.size())) cout<<"Yes";
    else cout<<"No";
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值