(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;
}