動態規划經典題之石子合並


題目描述

在一個園形操場的四周擺放N堆石子,現要將石子有次序地合並成一堆.規定每次只能選相鄰的2堆合並成新的一堆,並將新的一堆的石子數,記為該次合並的得分

試設計出1個算法,計算出將N堆石子合並成1堆的最小得分和最大得分

輸入輸出格式

輸入格式:

數據的第1行試正整數N,1≤N≤100,表示有N堆石子.第2行有N個數,分別表示每堆石子的個數

輸出格式:

輸出共2行,第1行為最小得分,第2行為最大得分

輸入輸出樣例

輸入樣例:

4
4 5 9 4

輸出樣例:

43
54

什么是區間動規

區間動態規划問題一般都是考慮,對於每段區間,他們的最優值都是由幾段更小區間的最優值得到,是分治思想的一種應用,將一個區間問題不斷划分為更小的區間直至一個元素組成的區間,枚舉他們的組合 ,求合並后的最優值

題解

由題意可得這些石子是擺放成一個環,環形不好寫狀態轉移方程,那我們就可以把它拆成一條長度為2*n-1 的鏈,采用區間動規的辦法,合並的石子數用前綴和優化一下就好了

狀態轉移方程:

f[i][j]表示從第i堆石子到第j堆石子的最大/最小得分,用k(i<=k<=j-1)將區間分成[i,k]和[k+1,j]兩個區間

初值:f[i][i]=0

fmax[i][j]=max{fmax[i][j],fmax[i][k]+f[k+1][j]+s[j]-s[i-1]}

fmin[i][j]=min{fmin[i][j],fmin[i][k]+f[k+1][j]+s[j]-s[i-1]}

代碼

#include<cstdio>
#include<cstring>
#define MAXN 2*100+5
#define INF 10000000

int fmax[MAXN][MAXN],fmin[MAXN][MAXN],s[MAXN],n,max_ans=-INF,min_ans=INF;

inline int maxx(int x,int y){return x>y?x:y;}
inline int minn(int x,int y){return x<y?x:y;}

void dp()
{
	int i,j,k;
	
	for(i=1;i<=2*n-1;++i)
	{
		for(j=1;j<=2*n-1;++j)
		{
			fmax[i][j]=-INF;
			fmin[i][j]=INF;
		}
	}
	for(i=1;i<=2*n-1;++i)
		fmax[i][i]=fmin[i][i]=0;
	
	for(i=2*n-1-1;i>=1;--i)
	{
		for(j=i+1;j<=2*n-1;++j)
		{
			for(k=i;k<=j-1;++k)
			{
				fmax[i][j]=maxx(fmax[i][j],fmax[i][k]+fmax[k+1][j]+s[j]-s[i-1]);
				fmin[i][j]=minn(fmin[i][j],fmin[i][k]+fmin[k+1][j]+s[j]-s[i-1]);
			}
		}
	}
}

void get_ans()
{
	int i;
	
	for(i=1;i<=n;++i)
	{
		//尋找長度為n的區間
        max_ans=maxx(max_ans,fmax[i][i+n-1]);
		min_ans=minn(min_ans,fmin[i][i+n-1]);
	}
}

int main()
{
	int i,temp;
	
	scanf("%d",&n);
	for(i=1;i<=n;++i)
	{
		scanf("%d",&temp);
		s[i]=s[i+n]=temp;
	}
	for(i=1;i<=2*n-1;++i)
		s[i]+=s[i-1];//前綴和優化
	
	dp();
	get_ans();
	
	printf("%d\n%d\n",min_ans,max_ans);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM