题解 - 合并石子
题解 - 合并石子
思路
这可以算是经典的区间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;
}
```