最小m段和问题(动态规划)

问题描述:给定n个整数组成的序列,现在要求将序列分割为m段,每段子序列中的数在原序列中连续排列。如何分割才能使这m段子序列的和的最大值达到最小?

问题说明:给定序列:5 4 3 2 1   n=5, m=2    可分为 5 | 4 | 3  2  1  其子序列和的最大值的最小值为1+2+3=6。

问题解析:用dp[i][j]存储长度为i,分j段后其子序列和的最大值的最小值,那么它由两部分构成:

                  当j=1时,dp[i][1]表示的是长为i的整个序列的和;

                  当j>1时,dp[i][j] = MIN(for(k=1; k<=i; k++)MAX(dp[k][j-1], dp[i][1] - dp[k][1]));这当中k表示的是分段的最后一段子序列的开始下标,所以dp[k][j-1]是前面j-1段子序列和的最大值的最小值,dp[i][1] - dp[k][1]是最后一段子序列的和。所以取这两段中的最大值,在k值变化过程中取得到的最小值就OK了。

代码:

#include 

int dp[100][100] ;					//dp[i][j]存储0~i的j个分组的和的最大值的最小值 

int MAX(int n1, int n2){
	
	if(n1 < n2)	
		return n2 ;
	else 
		return n1 ;
} 
 
void calculate(int a[], int n, int m){
	
	int i, j, k, temp, min ;

	if(n == 0){
		printf("数组为空!\n") ;
		return ;
	}

	for(i=1; i<=n; i++){			//当j=1时表示整个序列的和
		dp[i][1] = dp[i-1][1] + a[i] ;
	}

	for(i=1; i<=n; i++){
		
		for(j=2; j<=m; j++){
			
			min = 99999 ;			

			for(k=1; k<=i; k++){	//最后一段的每一次的分组情况

				temp = MAX(dp[k][j-1], dp[i][1] - dp[k][1]) ;
				
				if(temp < min){		//寻找最小值并赋值
					min = temp ;
				}
			}
			dp[i][j] = min ;
		}
	}
	printf("%d", dp[n][m]) ;
}

int main() 
{
	int a[100] ;
	int i, n, m ;

	printf("请输入数组长度:") ;
	scanf("%d", &n) ;	
	printf("请输入分段个数:") ;
	scanf("%d", &m) ;
	printf("请输入数组内容:") ;

	for(i=1; i<=n; i++){
		scanf("%d", &a[i]) ;
	} 
	
	calculate(a, n, m) ;

	printf("\n") ;

	return 0 ;
}

 

你可能感兴趣的:(算法设计与分析)