1、二分查找
已知有序的序列,比如:2,3,3,5,9,9,9,12,12,13,15,22,22,22,22,25,25,23,91,95
有整數x,比如: x=23
要求找到一個剛好比x稍微大一點的元素位置
當數組較大的時候,需要二分查找加快速度。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int f(int * a, int b, int e, int x){
if (e - b == 1){
if (a[b] > x){
return b;
}
else{
return e;
}
}
int m = (b + e) / 2;
if(x >= a[m]){
return f(a, m , e, x);
}
else{
return f(a, b, m, x);
}
}
int f1(int * a, int len, int x){
if(x > a[len - 1]){
return -1;
}
return f(a, 0, len, x);
}
int main(){
int a[] = {2,3,3,5,9,9,9,12,12,13,15,22,22,22,22,25,25,23,91,95};
printf("%d", f1(a, 20, 32));
}
2、最大序列和數組中整數有正有負
求一連續子段,使得和最大化
例如:
2,4,-7,5,2,-1,2,-4,3
最大連續段:
5,2,-1,2
其最大和爲8
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int f(int * a, int b, int e, int * k1, int * k2){
if(e - b == 1){
if(a[b] > 0){
return a[b];
}
return 0;
}
int c = (b + e) / 2;
int m1 = 0, m2 = 0, n1 = 0, n2 = 0;
int d1 = f(a, b, c, &m1, &m2);
int d2 = f(a, c, e, &n1, &n2);
int e1 = 0;
int e2 = 0;
int g = 0;
// *k1 = c;
// *k2 = c;
for (int i = c; i >= b; i--){
g += a[i];
if (g > e1){
e1 = g;
*k1 = i;
}
}
g = 0;
for(int i = c + 1; i < e; i++){
g += a[i];
if(g > e2){
e2 = g;
*k2 = i;
}
}
printf("k1 :%d k2 : %d\n", *k1, *k2);
int h = 0;
if(h < e1 + e2){
h = e1 + e2;
}
if(h < d1){
h = d1;
*k1 = m1;
*k2 = m2;
}
if(h < d2){
h = d2;
*k1 = n1;
*k2 = n2;
}
return h;
}
int main(){
int a[] = {2,4,-7,5,2,-1,2,-4,3};
int b = 0, c = 0;
printf("%d\n", f(a, 0, 9, &b, &c));
printf("%d %d\n", b, c);
for(int i = b; i <= c; i++){
printf("%d ", a[i]);
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int f(int * a, int b, int * d1, int * d2){
int d = 0;
for(int i = 0; i < b; i++){
for(int j = i; j < b; j++){
int c = 0;
for(int k = i; k <= j; k++){
c += a[k];
}
if(c > d){
d = c;
*d1 = i;
*d2 = j;
}
printf("i: %d j:%d %d\n", i, j, c);
}
}
return d;
}
int f2(int * a, int b, int * d1, int * d2){
int c = 0;
int d = 0;
*d1 = 0;
for(int i = 0; i < b; i++){
d += a[i];
if(d > c){
c = d;
*d2 = i;
}
if(d < 0){
d = 0;
*d1 = i + 1;
}
}
return c;
}
int main(){
int a[] = {2,4,-7,5,2,-1,2,-4,3};
int b, c;
printf("b:%d c:%d %d\n", b, c, f2(a, 9, &b, &c));
}
另外的兩種方法
最優起點
算法思想:設a[i]爲和最大序列的起點,則如果a[i]是負的,那麼它不可能代表最優序列的起點,因爲任何包含a[i]作爲起點的子序列都可以通過a[i+1]作起點而得到改進。參考(窗雨樹雪)
3、大數相乘
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
///字符串轉換成int 相乘
string f1(string a, string b){
stringstream c;
int aa;
int bb;
string cc;
c << a;
c >> aa;
c.clear();
c << b;
c >> bb;
c.clear();
c << aa * bb;
c >> cc;
return cc;
}
//字符串轉換成int 相加
string f4(string a, string b){
stringstream c;
int aa;
int bb;
string cc;
c << a;
c >> aa;
c.clear();
c << b;
c >> bb;
c.clear();
c << aa + bb;
c >> cc;
return cc;
}
// 在後面填0
string f3(int b){
if(b == 0){
return "";
}
if(b == 1){
return "0";
}
return f3(b / 2) + f3(b / 2) + f3(b % 2);
}
//不是分成兩段,從低位起每8 位一段
string add(string a, string b){
// cout << a << " " << b << endl;
if(a.length() <= 8 && b.length() <= 8){
return f4(a, b);
}
string a1 = "0";
string a2 = a;
if(a.length() > 8){
a1 = a.substr(0, a.length() - 8);
a2 = a.substr(a.length() - 8);
}
string b1 = "0";
string b2 = b;
if(b.length() > 8){
b1 = b.substr(0, b.length() - 8);
b2 = b.substr(b.length() - 8);
}
string c = add(a2, b2);
while(c.length() < 8) c = "0" + c;
if(c.length() > 8) return add(add(a1, b1), "1") + c.substr(1);
return add(a1, b1) + c;
}
string f(string a, string b){
if(a.length() < 5 && b.length() < 5){
return f1(a, b);
}
if(a.length() > 4){
int c = a.length() / 2;
string d = a.substr(0, c);
string e = a.substr(c);
return add(f(d, b) + f3(e.length()), f(e,b));
}
return f(b, a);
}
int main(){
// cout << add("111111111111111", "111111111111111111");
cout << f("1234567890987654321666", "1234567890123456789555") << endl;
}
4、城牆刷漆
X國的一段古城牆的頂端可以看成 2*N個格子組成的矩形(如圖所示)
現需要把這些格子刷上保護漆。
你可以從任意一個格子刷起,刷完一格,可以移動到和它相鄰的格子(對角相鄰也算數),但不能移動到較遠的格子(因爲油漆未乾不能踩!)
比如:a d b c e f 就是合格的刷漆順序。
c e f d a b 是另一種合適的方案。
當已知 N 時,求總的方案數。當N較大時,結果會迅速增大,請把結果對 1000000007 (十億零七) 取模。
輸入數據爲一個正整數(不大於1000)
輸出數據爲一個正整數。
例如:
用戶輸入:
2
程序應該輸出:
24
再例如:
用戶輸入:
3
程序應該輸出:
96
再例如:
用戶輸入:
22
程序應該輸出:
359635897
reference: 於航
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
long long int M = 1000000007;
long long f1(int n){
if(n == 1) return 1;
return f1(n - 1) * 2 % M;
}
long long f2(int n){
if(n == 1){
return 1;
}
if(n == 2){
return 6;
}
return (f1(n) + 2 * f2(n - 1) + 4 * f2(n - 2)) % M;
}
long long f3(int a, int b){
return (f1(a) * f2(b-a) * 2 % M + f1(b-a+1) * f2(a-1) * 2 % M) * 2 % M;
}
long long f(int n){
long long a = 0;
if(n == 1){
return 2;
}
a += f2(n) * 4 % M;
for(int i = 2; i < n; i++){
a = (a + f3(i, n))% M;
}
return a;
}
int main(){
// printf("%d", M);
for(int i = 1; i < 30; i++){
printf("%d\n", f(i));
}
}
5、環形塗色
如圖,組成環形的格子需要塗3種顏色。
它們的編號分別是1~14
相鄰的格子不能用相同的顏色。
塗色方案的數目是:24576
當格子數目爲50的時候,求塗色方案總數。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int f1(int n, int * a){ //f(n)中減去最後一個和第一個重複的,此時有n-1個格子,相當於減去f(n-1)
if(a[n] != 0) return a[n];
if(n == 1) return 1;
if(n == 2) return 6;
if(n == 3) return 6;
a[n] = 3 * pow(2, n - 1) - f1(n - 1, a);
return a[n];
}
int f(int n, int * a){
if(a[n] != 0) return a[n];
if(n == 1) return 1;
if(n == 2) return 6;
if(n == 3) return 6;
a[n] = 2 * f(n - 2, a) + f(n - 1, a);
return a[n];
}
int main(){
int a[60];
memset(a, 0, 60 * 4);
for(int i = 1; i < 20; i++){
printf("%d: %d\n", i, f1(i, a));
}
}
f(n) = f(n -1) + 2 * f(n - 2);
遞推關係:
1、假設第n-1塊的顏色和第1塊不同,那前n-1個方塊就是f(n-1)問題。由於第n塊的顏色既不能和第n-1塊顏色相同,也不能第1塊相同,那麼第n塊只能選擇剩下的那種顏色,唯一的一種選法。這時有f(n-1)種情況。
2、假設第n-1塊的顏色和第1塊是相同的,那知道第n-2塊的顏色必定和第1塊不同了,那前n-2個方塊就是f(n-2)問題。此時,第n塊有兩種選擇,只要顏色和第n-1塊的顏色不同即可。這時有2*f(n-2)種情況。
上面兩種情況綜合起來,就得到了遞推關係: f(n) = f(n-1) + 2*f(n-2)
reference: ojshilu