分治

分治的基本思路:

(1)划分问题:把问题的实际化为子问题;

(2)递归求解:递归解决子问题;

(2)合并问题:合并子问题得解.

举例:

1.给定一个数列,其中可能有正数也可能有负数,找出其中连续的一个子数列(不允许空序列),使它们的和尽可能大。

解法:

我们可以把整个序列平均分成左右两部分,答案则会在以下三种情况中:
1、所求序列完全包含在左半部分的序列中。
2、所求序列完全包含在右半部分的序列中。
3、所求序列刚好横跨分割点,即左右序列各占一部分。
前两种情况和大问题一样,只是规模小了些,如果三个子问题都能解决,那么答案就是三个结果的最大值。我们主要研究一下第三种情况如何解决:我们只要计算出:以分割点为起点向左的最大连续序列和、以分割点为起点向右的最大连续序列和,这两个结果的和就是第三种情况的答案。因为已知起点,所以这两个结果都能在O(N)的时间复杂度能算出来。
递归不断减小问题的规模,直到序列长度为1的时候,那答案就是序列中那个数字。

#include <bits/stdc++.h>

using namespace std;


// 区间左闭右开
int maxSum(int *A, int x, int y) {
    int m = x+(y-x)/2; // 分成两部分
    if(y-x == 1) return A[x]; // 如果只有一个数直接返回
    int ans = max(maxSum(A, x, m), maxSum(A, m, y)); // 全部存在于左边和全部存在于右边的情况取最大
    //cout << "debug: " << x << "&" << y << " # "<< ans << endl;
    int v, l, r;
    // 从分界点往左的最大和
    v = 0, l = A[m-1];
    for (int i = m-1; i >= 0; i--) {
        l = max(l, v += A[i]);
    }
    // 从分界点往右的最大和
    v = 0, r = A[m];
    for (int i = m; i < y; i++) {
        r = max(r, v += A[i]);
    }

    ans = max(ans, l+r);
    return ans;
}

int main() {
    int n;
    int A[105];
    cin >> n;
    for(int i = 0; i < n; i++) cin >> A[i];
    cout << maxSum(A, 0, n);
    return 0;
}
其他解法

(1) O(n^3)(暴力)

#include <bits/stdc++.h>

using namespace std;

int main() {
    int n, sum = 0, ans;
    int a[1000];
    cin >> n;
    for(int i = 0; i < n; i++) {
        cin >> a[i];
    }
    ans = a[0];
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            sum = 0;
            for (int k = i; k <= j; ++k) {
                sum += a[k];
            }
            ans = max(ans, sum);
        }
    }
    cout << ans << endl;
    return 0;
}

(2) O(n^2) (前缀和)

#include <bits/stdc++.h>

using namespace std;

int main() {
    int n, ans;
    cin >> n;
    int a[n+5], b[n+5];
    for(int i = 0; i < n; i++) {
        cin >> a[i];
    }
    b[0] = a[0];
    for (int i = 1; i < n; ++i) {
        b[i] = b[i-1] + a[i];
    }
    ans = a[0];
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            ans = max(ans, b[j] - b[i] + a[i]);
        }
    }
    cout << ans << endl;
    return 0;
}
// 1 2 3 4 5
//
// 2 4

(3) O(n)(dp)

#include <bits/stdc++.h>

using namespace std;

int main() {
    int n, ans;
    cin >> n;
    int a[n+5], dp[n+5];
    for(int i = 0; i < n; i++) {
        cin >> a[i];
    }
    dp[0] = a[0];
    ans = a[0];
    for (int i = 1; i < n; ++i) {
        if(dp[i-1] > 0) dp[i] = dp[i-1] + a[i];
        else dp[i] = a[i];
        ans = max(dp[i], ans);
    }

    cout << ans << endl;
    return 0;
}
/*
8
4 -3 5 -2 -1 2 6 -2
 * */

2.全排列
参考博文:https://blog.csdn.net/whealther/article/details/48878829
当n=1时,集合中只有一个元素;当n>1时,将整个数列中每一个数分别与第一个数交换后加其他数的全排列,因此每次都是处理后n-1个数的全排列(递归)
ps.必须交换两次, 交换完了后要交换回来,因为后面交换都是在原来的数列1,2,3...的基础上交换的,所以在排列前要交换一次,排列后还要交换一次(还原)

代码
#include <bits/stdc++.h>

using namespace std;

#define ll long long

int a[105];

void perm(int k, int m) {
    if(k == m) { // 只剩一个元素
        for(int i = 1; i <= m; i++) {
            printf("%d", a[i]);
            if(i != m)
                printf(" ");
        }
        printf("
");
        return;
    }
    
    for(int i = k; i <= m; i++) {
        swap(a[i], a[k]);
        perm(k+1, m); // 相当于以当前元素为首的全排列
        swap(a[i], a[k]);
    }
}

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {a[i] = i;} 
    perm(1, n);
    return 0;
}

3.整数划分

#include <bits/stdc++.h>

using namespace std;

#define ll long long

ll dp[100][100];

// 函数表示正整数n可以划分为不大于m的数的方法数
ll solve(int n, int m) {
    if(n < 1 || m < 1) return 0;
    if(dp[n][m]>0) return dp[n][m];  
    // m或者n为一,只有一种情况
    if(n == 1 || m == 1) dp[n][m] = 1;
    // m > n与m=n相同
    if(m > n) dp[n][m] = solve(n, n);
    // m=n时,用n组成m只有一种情况,在加上小于m的所有情况
    if((n != 1 && m != 1) && m == n) dp[n][m] = solve(n, m-1)+1;
    // m < n,emmmmmm看几个样例体会一下...
	/*
	6;
	5+1;
	4+2,4+1+1;
	3+3,3+2+1,3+1+1+1;
	2+2+2,2+2+1+1,2+1+1+1+1;
	1+1+1+1+1+1。
	*/
    if(m < n) dp[n][m] = solve(n, m-1) + solve(n-m, m);
    return dp[n][m];
}

int main() {
    int n;
    while(~scanf("%d", &n)) {
        memset(dp, 0, sizeof dp);
        solve(n, n);
        printf("%lld
", dp[n][n]);
    }
    return 0;
}


原文地址:https://www.cnblogs.com/knightoflake/p/14466163.html