zoukankan      html  css  js  c++  java
  • 动态规划1——通用切题思路

    y总的讲解视频 https://www.bilibili.com/medialist/play/watchlater/BV1X741127ZM

    关键词

    • 从集合角度来分析
    • 有限集中的最优化问题(最大值/最小值/个数/存在与否)
    • 自然的思路是指数级的,需要优化
    • 先化零为整,将一些有共同特征的元素化为一个子集,用 特定的状态 来表示,考察集合是什么,表示集合的元素存的内容是什么(一般情况就是题目问的对象)
    • 再化整为零,即 状态计算, 寻找最后一个不同点,将集合划分为若干个子集,保证不遗漏,不重复(视情况而定)
    • 始终抓住 集合的定义
    0-1背包问题

    原题链接 https://www.acwing.com/problem/content/2/

    //朴素写法  时间O(n^2)  空间O(n^2)
    #include <iostream>
    using namespace std;
    
    const int N = 1010;
    int n, m;
    int v[N], w[N];
    int f[N][N];  //f[i][j]表示所有只考虑前i个物品且总体积不超过j的方案中价值的最大值
    
    //递推公式
    //f[i][j] = max( f[i-1][j], (f[i - 1][j - v[i]] + w[i]) )
    //分成两个子集:1.包含最后一个物品; 2.不包含最后一个物品
    
    int main() {
        cin >> n >> m;
        
        for(int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
        
        for(int i = 1; i <= n; i ++ ) {
            for(int j = 0; j <= m; j ++ ) {
                f[i][j] = f[i - 1][j];
                if(j >= v[i]) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]); //在背包容量大于v[i]的前提下,才能递推
            }
        }
        
        cout << f[n][m] << endl;  //最大价值是考虑所有n个物品且总体积不超过m的方案中的最大价值
        return 0;
    }
    
    //优化空间复杂度写法 时间O(n^2)  空间O(n)
    //滚动数组,去掉第一维,因为每次计算当前层时,只需要用到上一层的结果
    //dp问题的所有优化,都是对代码做等价变形
    #include <iostream>
    using namespace std;
    
    const int N = 1010;
    int n, m;
    int v[N], w[N];
    int f[N];
    
    int main() {
        cin >> n >> m;
        
        for(int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
        
        for(int i = 1; i <= n; i ++ ) {
            for(int j = m; j >= v[i]; j -- ) { //这儿j从大到小循环,保证了下面求递推时,f[j - v[i]]是上一层循环计算出来的值,即f[i - 1][j - v[i]],在这层还未被更新
                if(j >= v[i]) f[j] = max(f[j], f[j - v[i]] + w[i]);
            }
        }
        
        cout << f[m] << endl;
        return 0;
    }
    
    完全背包问题

    原题链接 https://www.acwing.com/problem/content/3/

    /*
    1. 01背包:  f[i][j] = max(f[i - 1][j], f[i - 1][j - vi] + wi)
    2. 完全背包:f[i][j] = max(f[i - 1][j], f[i][j - vi] + wi);
    */
    //朴素写法是需要一个二维矩阵来表示状态的,但是通过观察分析模拟过程,我们发现空间是可以复用的,很早之前的状态后续不再需要
    //我们只需要保留后续计算还需要的值,可以用一个“滚动数组”来替代二维数组
    //背包问题如果用二维矩阵记录状态,第二层的循环顺序不受限制,如果优化成滚动数组,则01背包从大到小循环,完全背包从小到大
    //具体来讲,考察当前计算所需的那个状态需不需要更新
    
    #include <iostream>
    using namespace std;
    
    const int N = 1010;
    int n, m;
    int v[N], w[N];
    int f[N]; // 在考虑边界和状态转移时始终抓住这点定义!!!
    
    int main() {
        cin >> n >> m;
        
        for(int i  = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
        
        for(int i = 1; i <= n; i ++ ) {
            for(int j = v[i]; j <= m; j ++ ) { //与0-1背包唯一一句区别
                f[j] = max(f[j], f[j - v[i]] + w[i]);
            }
        }
    	
        cout << f[m] << endl;
        return 0;
    }
    
    石子合并(区间dp)

    原题链接:https://www.acwing.com/problem/content/284/

    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int N = 310;
    int n;
    int s[N];
    int f[N][N]; //f[i][j]表示将[i, j]合并成一堆的方案的集合中的最小代价
    
    int main() {
        cin >> n;
        for(int i = 1; i <= n; i ++ ) cin >> s[i];
        
        for(int i = 1; i <= n; i ++ ) s[i] += s[i - 1];  //求前缀和
        
        for(int len = 2; len <= n; len ++ ) //枚举区间长度
            for(int i = 1; i + len - 1 <= n; i ++ ) { //枚举起点
                int l = i, r = i + len - 1;
                f[l][r] = 2e9;
                for(int k = l; k < r; k ++ )//枚举分界点
                    f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]);
            }
            
        cout << f[1][n] << endl;
        return 0;
    }
    
    最长公共子序列

    原题链接:https://www.acwing.com/problem/content/899/

    //求数量要求不重不漏,求最值得时候可以重复
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int N = 1010;
    int n, m;
    char a[N], b[N];
    int f[N][N];  //f[i][j]表示所有 a[1 - i]与  b[1 - j]的公共子序列的集合当中的最大长度
    
    int main() {
        cin >> n >> m;
        scanf("%s%s", a + 1, b + 1);
        
        for(int i = 1; i <= n; i ++ ) {
            for(int j = 1; j <= m; j ++ ) {
                f[i][j] = max(f[i - 1][j], f[i][j - 1]);  //这儿的状态表示和子集并不是完美对应的,状态表示的范围大于实际需要表示的子集,但是因为是求最大值,所以不会影响最终结果
                if(a[i] == b[j]) f[i][j] = max(f[i - 1][j - 1] + 1, f[i][j]);
            }
        }
        
        cout << f[n][m] << endl;
        return 0;
    }
    
  • 相关阅读:
    总结:Sharepoint2010 Client Object Model Managed Client
    学习:SharePoint验证控件
    学习:Javascript与后台交互(转)
    总结:sharepoint webservice开发常见错误
    Xtreme ToolkitPro 版本更新至 2007 Volume 1
    WebUI Studio.NET® 优秀用户界面控件套装
    Spread v7.0 表格控件中的领头羊
    WebUI Studio.NET® 2007 R1 用户界面套装新版本发布,全面支持AJAX技术
    Skelta SharePoint Accelerator
    磐岩科技控件中国网乔迁新址
  • 原文地址:https://www.cnblogs.com/huhu555/p/14628084.html
Copyright © 2011-2022 走看看