题解 - 合并石子

题解 - 合并石子

思路

这可以算是经典的区间DP了吧,思路很好想,但状态转移方程推出来后发现它依赖[i][j]并没有顺序性,所以有两个解决方案,第一是记忆化搜索,第二是将区间长度作为状态进行DP,这样,因为状态转移方程中用到的区间长度小于待求区间,所以它们一定已经求解过了。

代码

#include <iostream>
#include <cstring>
using namespace std;
int n;
long long ar[310],sum[310],tmp[310][310],dp[310][310];
long long dpmax(int x,int y)
{
	//long long ans = 1LL << 60;
	long long ans = 0;
	for(int k = x; k < y; ++k)
		ans = max(ans,dpmax(x,k) + dpmax(k+1,y) + sum[y] - sum[x-1]);
	return ans;
 } 
 long long dpmin(int x,int y)
{
	long long ans = 1LL << 60;
	if(x == y)return 0;
    if(tmp[x][y])return tmp[x][y];
	//long long ans = 0;
	for(int k = x; k < y; ++k)
		ans = min(ans,dpmin(x,k) + dpmin(k+1,y) + sum[y] - sum[x-1]);
    tmp[x][y] = ans;
	return ans;
} 
int main()
{
	
	cin >> n;
	for(int i = 1; i <= n; ++i)
		cin >> ar[i];
	for(int i = 1; i <= n; ++i)	
		sum[i] = sum[i-1] + ar[i];
	//for(int i = 1; i <= n; ++i)	cout << sum[i] << " ";
	//cout << dpmin(1,n);
    memset(dp, 0X3f, sizeof(dp)); // 0X7f是最大值,0X3F即使有两个数相加也不会越界
    for(int i = 1; i <= n; ++i)dp[i][i] = 0;//第i组到第i组不用合并
    for(int len = 1; len <= n; ++len)
    {
        for(int i = 1; i + len-1 <= n; ++i)
        {
            int j = i + len -1;
            for(int k = i; k < j; ++k){
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]);
                // cout << i << j << ": " << dp[i][j];
            }
            // cout << endl;
        }
    }
    // for(int i = 1; i <= n; ++i)
    // {
    //     for(int j = i; j <= n; ++j)
    //         cout << dp[i][j] << " ";
    //     cout << endl;
    // }
    cout << dp[1][n] << endl;
	return 0;	
}
```
posted @ 2021-12-10 18:38  边缘坐标  阅读(22)  评论(0编辑  收藏  举报
-->