1、k进制数转换为10进制数:例如k进制数abcdef(这是一个6位数)它就是a*k^5+b*k^4+c*k^3+d*k^2+e*k^1+f*k^0
1、散列:将元素通过一个函数转换为整数,使得该整数可以尽量唯一地代表这个元素。
2、直接把输入的数作为数组的下标来对这个数的性质进行统计(很实用)或是线性变换(即H(key)=a*key+b)
3、除留余数法:是指把key除以一个数mod得到的余数作为hash值的方法:H(key)=key%mod;
4、假设字符串均由大写字母A~Z组成,在这个基础上,把A~Z视为0~25,这样就把26个大写字母对应到了26进制中
将26进制转换为10进制
int hashfunc(char S[],int len)//hash函数,将字符串s转换成整数
{
int id=0;
for(int i=0;i
如果字符串中出现了小写字母,把A~Z视为0~25,把a~z视为26~51,这样就变成了52进制转换为10进制
int hashfunc(char S[],int len)//hash函数,将字符串s转换成整数
{
int id=0;
for(int i=0;i='A'&&S[i]<='Z')
{
id=id*52+(S[i]-'A');
}
else if(S[i]>='a'&&S[i]<='z')
{
id=id*52+(S[i]-'a')+26;
}
}
return id;
}
hash函数的应用
//给出N个字符串(由恰好三位大写字母组成),再给出M个查询字符串,问每个查询字符串在N个字符串中出现的次数.所以函数实参为3
#include
const int maxn=100;
char S[maxn][5],temp[5];
int hashTable[26*26*26+10];
int hashFunc(char S[],int len)//hash函数,将字符串S转换成整数
{
int id=0;
for(int i=0;i
递归之全排列
#include
using namespace std;
const int mod = 1e9+7;
#define ll long long
const double eps = 1e-8;
const int maxn = 11;
#define lowbit(x) (x&-x)
int p[maxn],hashtable[maxn]= {false};//p为当前排列,hashtable记录整数x是否已经在p中
int n;
void gen(int index)
{
if(index==n+1)//注意这里是==
{
for(int i=1; i<=n; i++)
{
cout<
回溯法之n皇后问题
一般来说,如果在到达递归边界前的某层,由于一些事实导致已经不需要往任何一个问题递归,就可以直接返回上一层,一般把这种做法叫做回溯法。
int count=0;
void gen(int index)
{
if(index=n+1)//递归边界,生成一个合法方案
{
count++;
return;
}
for(int x=1;x<=n;x++)
{
if(hashtable[x]==false)
{
bool flag=true;
for(int pre=1;pre
只对价格从高到低排序,这个方法一定要记住
struct node
{
double zhu;
double shou;
double dan;
}cake[maxn];
bool cmp(node a,node b)
{
return a.dan>b.dan;
}
sort(cake,cake+N,cmp);
区间贪心:即区间不相交问题:给出N个开区间(x,y),从中选择尽可能多的开区间,使得这些开区间两两没有交集
(总是选择左端点最大的区间,在左端点相同的情况下,按右端点从小到大排序)。
//区间贪心:即区间不相交问题:给出N个开区间(x,y),从中选择尽可能多的开区间,使得这些开区间两两没有交集
//(总是选择左端点最大的区间,在左端点相同的情况下,按右端点从小到大排序)。
#include
using namespace std;
const int mod = 1e9+7;
#define ll long long
const double eps = 1e-8;
const int maxn = 110;
#define lowbit(x) (x&-x)
struct interval
{
int x,y;//开区间左右端点
}I[maxn];
bool cmp(interval a,interval b)
{
if(a.x!=b.x)
return a.x>b.x;//先按左端点从大到小排序
else
return a.y>I[i].x>>I[i].y;
}
sort(I,I+n,cmp);//把区间排序
//ans记录不相交区间个数,lastX记录上一个被选中区间的左端点
int ans=1,lastX=I[0].x;
for(int i=1;i
1、函数返回第一个大于等于x的元素的位置,lower_bound
//A[]为递增序列,x为欲查询的数,函数返回第一个大于等于x的与元素的位置
//二分上下界为左闭右闭的[left,right],传入的初值为[0,n];
int lower_bound(int A[],int left,int right,int x)
{
int mid;//mid为left和right的中点
while(left=x)//中间的数大于等于x
right=mid;//往左子区间[left,mid]查找
else//中间的数小于x
left=mid+1;
}
return left;//返回的时候:是left==right才会返回,所以返回值既可以是left也可以是right
}
2、求序列中第一个大于x的元素的位置,upper_bound
//A[]是递增序列,x为欲查询的数,函数返回第一个大于x的元素的位置
//二分上下界为左闭右闭的[left,right],传入初值为[0,n]
int upper_bound(int A[],int left,int right,int x)
{
int mid;//mid为left和right的中点
while(leftx)
{
right=mid;
}
else//中间的数小于等于x
left=mid+1;
}
return left;
}
3、lower_bound与upper_bound函数都在解决一个这样问题:寻找有序序列中第一个满足某条件的位置。。
二分法拓展
计算根号2的近似值,以精确到10的-5为例
const double eps=le-5;
double f(double x)
{
return x*x;
}
double calsqrt()
{
double left=1,right=2,mid;
while(right-left>eps)
{
mid=(left+right)/2;
if(f(mid)>2)
{
right=mid;
}
else
left=mid;
}
return mid;
}
1、快速幂的递归写法( 条件 if(b%2==1)可以用if(b&1)进行位与操作,判断b的末尾是否为1,因此当b为奇数时b&1返回1,if条件成立)
typedef long long LL;
//求a^b%m,递归写法
LL binaryPow(LL a,LL b,LL m)
{
if(b==0)
return 1;
//b为奇数
if(b%2==0)//if(b&1)
{
return a*binaryPow(a,b-1,m)%m;
}
else
{
//b为偶数,转换为b/2
LL mul=binaryPow(a,b/2,m);
return mul*mul%m;
}
}
2、快速幂的迭代写法
typedef long long LL;
//求a^b%m,迭代写法
LL binaryPow(LL a,LL b,LL m)
{
LL ans=1;
while(b>0)
{
if(b&1)//如果b的二进制末尾为1(也可以写成if(b%2))
{
ans=ans*a%m;//令ans累计上a
}
a=a*a%m;//令a平方
b>>=1;//将b的二进制右移一位,即b=b>>1或b=b/2
}
return ans;
}
1、给定一个递增的正整数序列和一个正整数M,求序列中的两个不同位置的数a和b,使得它们的和恰好为M,输出所有满足条件的方案。
//令下标i的初值为0,下标j的初值为n-1
while(i
2、序列合并问题:假设有两个递增序列A与B,要求将它们合并为一个递增序列C。同样的,可以设置两个下标i和j,初值均为0,表示分别指向序列A的第一个元素和序列B的第一个元素,然后根据A[i]与B[j]的大小来决定哪一个放入序列C
int merge(int A[],int B[],int C[],int n,int m)
{
int i=0,j=0,index=0;//i指向A[0],j指向B[0]
while(i
1、原理:将序列两两分组,将序列归并为[n/2]个组,组内单独排序;然后将这些组再两两归并,生成[n/4]个组,组内再单独排序;依次类推,直到只剩下一个组为止。
递归实现:
const int maxn=100;
//将数组A的[L1,R1],与[L2,R2].区间合并为有效区间,(此处L2即为R1+1)
void merge(int A[],int L1,int R1,int L2,int R2)
{
int i=L1,j=L2;//i指向A[L1],j指向A[L2]
int temp[maxn],index=0;//temp临时存放合并后的数组,index为其下标
while(i<=R1&&j<=R2)
{
if(A[i]<=A[j])
{
temp[index++]=A[i++];
}
else
{
temp[index++]=A[j++];
}
}
while(i<=R1)
temp[index++]=A[i++];//将[L1,R1]的声誉元素加入序列temp
while(J<=R2)
temp[index++]=A[j++];
for(i=0;i
1、平均复杂度为:O(nlogn);
思路:①调整序列中的元素,使当前序列最左端的元素在调整后满足左侧所有元素均不超过该元素、右侧所有元素均大于该 元素
②对该元素的左侧和右侧分别递归进行①的调整,直到当前调整区间的长度不超过1
2、递归实现
//对区间[left,right]进行划分
int Partition(int A[],int left,int right)
{
int temp=A[left];
while(lefttemp)
right--;
A[left]=A[right];//将A[right]挪到A[left]
while(left
3、生成随机数:如果想要输出给定范围[a,b]内的随机数,需要使用rand()%(b-a+1)+a。因为显然rand()%(b-a+1)的范围是[0,b-a],再加上a之后就是[a,b].
eg: 生成[0,1] 与[3,7]范围内的随机数
#include
#include
#include
int main()
{
srand((unsigned)time(NULL));
for(int i=0;i<10;i++)
printf("%d ",rand()%2);//[0,1];
printf("\n");
for(int j=0;j<10;j++)
{
printf("%d ",rand()%5+3);//[3,7]
}
return 0;
}
4、选取随机主元,对区间[left,right]进行划分
//选取随机主元,对区间[left,right]进行划分
int randPartition(int A[],int left, int right)
{
//生成[left,right]内的随机数p
int p=(round(1.0*rand()/RAND_MAX*(right-left)+left);
swap(A[p],A[left]);//交换A[p]和A[left]
//以下为原先Partition函数的划分过程,不需要任何东西
int temp=A[left];//将A[left]存放在临时变量temp
while(lefttemp)
right--;
A[left]=A[right];
while(left
随机选择算法:给定一个由整数组成的集合,集合中的整数各不相同,现在要将它分为两个子集合,使得这两个子集合的并为原集合、交为空集,同时在两个子几何的元素个数n1与n2之差的绝对值尽可能小的前提下,要求它们各自的元素之和S1与S2之差的绝对值尽可能大,求这个S1-S2的绝对值等于多少?
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=100010;
int A[maxn],n;//A存放所有整数,n为其个数
//选取随机主元,对区间[left,right]进行划分
int randPartition(int A[],int left,int right)
{
//生成[left,rigth]内的随机数p
int p=(round(1.0*rand()/RAND_MAX*(right-left)+left));
swap(A[p],A[left]);
int temp=A[left];
while(lefttemp)
right--;
A[left]=A[right];
while(left>n;//整数个数
for(int i=0;i>A[i];
sum+=A[i];
}
randSelect(A,0,n-1,n/2);//寻找第n/2的数,并进行划分
for(int i=0;i