循环扫描
问题:存在一个序列,我们需要循环地从头扫到尾,并且这个序列的大小可能会变动。
常规思路:用整数 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);\}
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 ;
}
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)
( n A − n B ) 尽量小,且A,B的和的差
(
s
u
m
A
−
s
u
m
B
)
(sum_A-sum_B)
( s u m A − s u m 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;
}
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) ;
}