• 7-1 最大子列和问题 (20分)


    7-1 最大子列和问题 (20分)

    给定(K)个整数组成的序列({N_1, N_2, ..., N_K​}),“连续子列”被定义为({ N_i, N_{i+1}, ..., N_j}),其中(1≤i≤j≤K)。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。

    本题旨在测试各种不同的算法在各种数据情况下的表现。各组测试数据特点如下:

    数据1:与样例等价,测试基本正确性;
    数据2:102个随机整数;
    数据3:103个随机整数;
    数据4:104个随机整数;
    数据5:105个随机整数;

    输入格式:

    输入第1行给出正整数(K (≤100000));第2行给出(K)个整数,其间以空格分隔。

    输出格式:

    在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。

    输入样例:

    6
    -2 11 -4 13 -5 -2
    

    输出样例:

    20

    思路

    这是一道动态规划的题目。
    先来个dp数组,是一维的还是二维的?
    首先,不读完整个序列,无法确定连续最大子列。

    1. 尝试递归
      在读入第i个数,就确定了,直到i的连续最大子列,和也知道。
      那么读入第i+1个数。既然是最大,那么负数一定要排除。所以第i个数若是负数(情况1),此时必定不是最大子列的元素。
      第i个数若是正数,也不一定是最大子列的元素,因为可能,最大子列吸收它的代价太高。
      看第i+1个数,(情况1),如果能够补偿i的损失。
      (情况2),能够弥补吸收的代价。
      失败。
    2. 尝试递推
      这里的底层元素,显然就是每一个数本身。
      性质分类就是 + - 。
      如果i以后的数和为正,那么最大子列会吸收后续所有
      最大连续子列

    9:15 2019/1/23 第二天思考。
    dp数组应该是一维的。
    对dp[i]先做初始化,在开头遇到的负数,其dp都为0
    遇到第一个正数,及之后的所有数,dp为其本身。

    /*这是主要方程,但不是全部*/
    dp[i] = dp[i+1]+a[i];
    

    10:34 2019/1/23 成功AC,对状态转移方程做了详细的定义。
    分析了4种情况。我的想法是让人“未卜先知”,或者说是看到未来,所以是从最后一位数的dp来思考的。dp记录的是最大连续子列吸收该数的后果。要做到这一点,必须已经读入整个序列,从最后一个数开始考虑。因为随着后续数的不同,对某个数吸收与否的命运会改变。
    如果已经知道吸收一个数的后果,就可以判断是否应该吸收了。
    同时,吸收该数,希望吸收最小的坏后果,所以要对(a[i]>0 && dp[i+1]<0)特判,这种情况下,最优策略是仅仅吸收该整数本身。

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    int main(void){
    	int n,i=0,sum=0;
    	cin>>n;
    	vector<int> dp(n),a(n);
    	/*初始化dp数组*/
    	bool flag = false;/*代表遇到正数以后*/
    	while(cin>>a[i]){/*调试时,需要ctrl+z*/
    		if(a[i]>0){
    			flag =true;
    			dp[i] = a[i];
    		}
    		else if(flag){
    			dp[i] = a[i];
    		}
    		else{
    			dp[i] = 0;
    		}
    		i++;
    	} 
    //	for(int i =0;i<n;i++){
    //		cout<<a[i]<<endl;
    //		cout<<dp[i]<<endl;
    //	}
    		
    	/*成功读入*/
    	/*状态转移方程*/
    	for(int i=n-2;i>=0;i--){
    		if(a[i]>0 && dp[i+1]<0){
    			continue;
    		}
    		else
    			dp[i] = dp[i+1] + a[i]; 
    	}
    	
    	cout<<dp[0];
    	return 0;
    }
    

    参考胡凡的书,这里对状态转移方程可以优化一波。

    for(int i=n-2;i>=0;i--){
    	dp[i] = max(a[i],dp[i+1] + a[i]); 
    }
    

    我的状态转移方程也是满足无后效性的。
    从头开始的动态规划,就是定义了dp数组元素dp[i]为以a[i]结尾的连续子列最大和。
    dp[i] = max(a[i],dp[i-1] + a[i]);
    对最大和条件做了弱化,或者说是定义修改。
    接着找出,这些和中值最大的那一个。

  • 相关阅读:
    Module not found: Error: Can't resolve './style':配置 extensions 的坑
    Mysql5.7前后修改用户密码变化
    一步步分析Java深拷贝的两种方式clone和序列化
    Windows下Nodejs的开发环境搭建
    JavaNIO第一话Buffer
    Vscode浏览器打开html vscode修改默认浏览器
    Windows电脑多个SSH Key管理.md
    jsonp跨域的原理
    POJ1502(MPI Maelstrom)
    POJ1088(滑雪)
  • 原文地址:https://www.cnblogs.com/lingr7/p/10307778.html
Copyright © 2020-2023  润新知