石子合并问题(动态规划)

在一个操场上摆放着一行共n堆的石子。现要将石子有序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆石子数记为该次合并的得分。请编辑计算出将n堆石子合并成一堆的最小得分和将n堆石子合并成一堆的最大得分。

容易想到贪心法,但可惜是错误的。

需要用到动态规划,类似于矩阵链乘。

#include 
#include 
#include 
using namespace std;
const int MAX_N = 101;
const int INF = 0x3f3f3f3f;
int n;
int a[MAX_N];
int d[MAX_N][MAX_N];
int sum[MAX_N];


void solve()
{
	//init sum[]
	sum[0] = a[0];
	for ( int i = 1; i < n; i++ )
	{
		sum[i] = sum[i - 1] + a[i];
	}

	//init dp
	for ( int i = 0; i < n; i++ )
	{
		for ( int j = 0; j < n; j++ )
		{
			if ( i == j )
			{
				d[i][j] = 0;
			}
			else
			{
				d[i][j] = -INF;
			}
		}
	}

	//dp
	for ( int v = 1; v < n; v++ )
	{
		for ( int i = 0; i < n - v; i++ )
		{
			int j = i + v;
			int s = sum[j] - ( i > 0 ? sum[i-1] : 0 );
			for ( int k = i; k < j; k++ )
			{
				d[i][j] = max ( d[i][j], d[i][k] + d[k + 1][j] + s );
			}
		}
	}

	printf ( "%d\n", d[0][n - 1] );
}

int main()
{
	freopen ( "in.txt", "r", stdin );
	scanf ( "%d", &n );
	for ( int i = 0; i < n; i++ )
	{
		scanf ( "%d", a + i );
	}

	solve();

	return 0;
}


--------------------------

环形的:将环变成2*n的线型

#include 
#include 
using namespace std;

const int MAX_N = 101, INF = 0x3f3f3f3f;
int n;
bool isLoop = false;
int a[MAX_N*2];
int sum[MAX_N*2];
int d[MAX_N*2][MAX_N*2];

void solve()
{
	//init sum array
	for ( int i = 0; i < 2 * n; i++ )
	{
		if ( i == 0 )
		{
			sum[0] = a[0];
		}
		else
		{
			sum[i] = sum[i - 1] + a[i];
		}
	}

	//init dp array
	for ( int i = 0; i < 2 * n; i++ )
	{
		for ( int j = 0; j < 2 * n; j++ )
		{
			if ( i == j )
			{
				d[i][j] = 0;
			}
			else
			{
				d[i][j] = -INF;
			}
		}
	}

	for ( int v = 1; v < n; v++ )
	{
		for ( int i = 0; i < 2 * n - v; i++ )
		{
			int j = i + v;
			int s = sum[j] - ( i > 0 ? sum[i - 1] : 0 );
			for ( int k = i; k < j; k++ )
			{
				d[i][j] = max ( d[i][j], d[i][k] + d[k + 1][j] + s );
			}
		}
	}
	int ans = -INF;
	for ( int i = 0; i < n + 1; i++ )
	{
		ans = max ( ans, d[i][n - 1 + i ] );
	}
	printf ( "%d\n", ans );
}

int main()
{
	freopen ( "in.txt", "r", stdin );
	while ( ~scanf ( "%d", &n ) && n )
	{
		isLoop = true;
		for ( int i = 0; i < n; i++ )
		{
			scanf ( "%d", a + i );
		}

		if ( isLoop )
		{
			for ( int i = n; i < 2 * n; i++ )
			{
				a[i] = a[i - n];
			}
		}

		solve();
	}

}

平行四边形法则优化:

#include 
#include 
#include 
using namespace std;
const int MAX_N = 101;
const int INF = 0x3f3f3f3f;
int n;
int a[MAX_N];
int d[MAX_N][MAX_N];
int K[MAX_N][MAX_N];
int sum[MAX_N];


void solve()
{
	//init sum[]
	sum[0] = a[0];
	for ( int i = 1; i < n; i++ )
	{
		sum[i] = sum[i - 1] + a[i];
	}

	//init dp
	for ( int i = 0; i < n; i++ )
	{
		for ( int j = 0; j < n; j++ )
		{
			if ( i == j )
			{
				d[i][j] = 0;
				K[i][j] = i;
			}
			else
			{
				d[i][j] = -INF;
			}
		}
	}

	//dp
	for ( int v = 1; v < n; v++ )
	{
		for ( int i = 0; i < n - v; i++ )
		{
			int j = i + v;
			int s = sum[j] - ( i > 0 ? sum[i - 1] : 0 );
			//for ( int k = i; k < j; k++ )
			//{
			//	d[i][j] = max ( d[i][j], d[i][k] + d[k + 1][j] + s );
			//}
			int Max = -INF;
			for ( int k = K[i][j - 1]; k <= K[i + 1][j]; k++ )
			{
				if ( Max < d[i][k] + d[k + 1][j] + s )
				{
					Max = d[i][k] + d[k + 1][j] + s;
					K[i][j] = k;
				}
			}
			d[i][j] = Max;
		}
	}

	printf ( "%d\n", d[0][n - 1] );
}

int main()
{
	freopen ( "in.txt", "r", stdin );
	scanf ( "%d", &n );
	for ( int i = 0; i < n; i++ )
	{
		scanf ( "%d", a + i );
	}

	solve();

	return 0;
}


你可能感兴趣的:(动态规划)