• 分治


    分治的基本思路:

    (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;
    }
    
    
    
  • 相关阅读:
    ajax 中文乱码问题 主要是IE浏览器
    js 带省略号的分页源码及应用实例
    js iframe onload &line-height浏览器兼容问题
    Js 正则表达式特殊字符含义
    js prototype
    js call apply caller callee bind
    怎么查看一个网站是用什么语言编写的?
    ASP.NET之AreaRegistration
    产品经理
    程序猿全面充电10本书
  • 原文地址:https://www.cnblogs.com/knightoflake/p/14466163.html
Copyright © 2020-2023  润新知