算法--技巧

循环扫描

  • 问题:存在一个序列,我们需要循环地从头扫到尾,并且这个序列的大小可能会变动。
  • 常规思路:用整数 i 作为指针,L为序列的长度,(i++)%L 代表下一个扫描的数,注意可能更新L
  • 其它思路:默认 i = 0(隐含指针指向头部),不断地将第一个数移到最后
  • 例:n个小朋友围成一个圈,顺时针依此编号为1~n,现在从小朋友1开始报数1,顺时针依此报数,报数依此递增。等小朋友数到k的倍数或者个位是k的时候,该小朋友被淘汰出局,求最后剩下的小朋友
int solve(int n, int k){
	queue<int> sts;
	for(int i = 1;i<=n;i++){
		sts.push(i);
	}
	int num = 1;
	while(sts.size()>1){
		if(num %10 == k || num%k==0){
			sts.pop();
		}else{
			sts.push(sts.front());
			sts.pop();
		}
		num ++;
	}
	
	return sts.front();
}

排列数

  • 可以直接用algorithm中的next_permutation 或prev_permutation
	int mset[] = {1,4,3};
	sort(mset, mset+3);
	do{
		cout<<mset[0]<<mset[1]<<mset[2]<<endl;
	}while(next_permutation(mset,mset+3));

组合数

  • 组合式可以看作排列数的一个子集,选取排列数中所有递增或递减排列的元素就可以构成组合数。所以在求解组合式时,不妨让整个序列是递增的。
  • 例:求 从1 ~ n 这n个数中选择k个,打印出每种选择的结果:设f(n,k)表示从1 ~ n中选k个数。
    f ( n , k ) : =           k = = 0 : = i k n { a [ k 1 ] = i ; f ( i 1 , k 1 ) ; } f(n,k) := 打印结果\ \ \ \ \ \ \ \ \ k==0\\:=i的取值从k到n \{a[k-1]=i;f(i-1,k-1);\}
#include<iostream>
using namespace std;
int combs[3];
void comb(int n,int k);
int main(){
	comb(5,3);
	return 0;
}

// k--第k个组合数, n--第k个组合数的最大值 
void comb(int n, int k){
	if(k==0){
		cout<<combs[0]<<combs[1]<<combs[2]<<endl;
		return;
	}
	for(int i=k;i<=n;i++){
		cpmbs[k-1] = i;
		comb(i-1, k-1);
	}

利用快排的原理求k_th

  • 例:对于一个序列S,分割成序列A、B, 要求A, B的个数 n A n B (n_A-n_B) 尽量小,且A,B的和的差 ( s u m A s u m B ) (sum_A-sum_B) 尽量大,求最大的差值
#include<iostream>
using namespace std;

int a[]={4,5,8,9,3};
int n=5;
int partition(int l, int r);
int solve();

int main(){
	cout<<partition(0,n-1)<<endl;
	cout<<solve()<<endl;
	
	return 0;
} 
// 快排的划分 
int partition(int l, int r){
	int left = l, right = r;
	int x=a[l];
	while(left<right){
		while(a[right]>x && left<right){
			right--;
		}
		a[left] = a[right];
		while(a[left]<=x && left<right){
			left++;
		}		
		a[right] = a[left];		
	}
	a[left]=x;
	return left;
}
// 找到第n/2的元素作为分割 
int solve(){
	int l=0, r=n-1;
	int k;
	while(true){
		k = partition(l,r);
		if(k==n/2){
			break;
		}else if(k<n/2){
			l = k+1;
		}else{
			r = k-1;
		}	
	}
	cout<<"k:"<<k<<endl;
	int sum1=0,sum2=0;
	for(int i=0;i<k;i++){
		sum1 += a[i];
	}
	for(int i=k;i<n;i++){
		sum2 += a[i];
	}
	return sum2-sum1;
}

利用归并排序求逆序数

#include<iostream>
using namespace std;
int a[] = {3,8,4,6,10,2,5,1,7,9};
int n=10;
void merge(int left, int mid, int right);
void mergeSort(int left, int right);
// 逆序数 
int invNum = 0;

int main(){
	mergeSort(0,n-1);
	cout<<invNum<<endl;
	return 0;
}
// 归并 
void merge(int left, int mid, int right){
	int *tmp = new int[right-left+1];
	int l1 = left, l2=mid+1;
	int i=0;
	while(l1<=mid && l2<=right){
		if(a[l1] <= a[l2])
			tmp[i++] = a[l1++];		
		else if(a[l1] > a[l2]){
			invNum += mid-l1+1;
			tmp[i++] = a[l2++];	
		}
	}
	while(l1<=mid){
		tmp[i++] = a[l1++];
	}
	while(l2<=right){
		tmp[i++] = a[l2++];
	}
	for(int j=0; j<i; j++){
		a[left+j] = tmp[j];
	}
	delete [] tmp;
}

void mergeSort(int left, int right){
	if(left>=right){
		return;
	}
	int mid = (left+right)/2;
	mergeSort(left,mid);
	mergeSort(mid+1,right);
	merge(left,mid,right);
}
发布了56 篇原创文章 · 获赞 2 · 访问量 497

猜你喜欢

转载自blog.csdn.net/qq_41956860/article/details/103300146