题目
题意给排在一排的n堆石子将其合并
输出其最小价值
输入 :
4
1 3 5 2
输出:
22
分析
第一阶段:
dp[1][1],dp[2][2],dp[3][3],dp[4][4]
因为一开始还没有合并,所以这些值应该全部为0。
第二阶段:
两两合并过程如下,其中sum(i,j)表示石头的数量,即从i开始数j个数的和
dp[1,2]=dp[1,1]+dp[2,2]+sum[1,2];
dp[2,3]=dp[2,2]+dp[3,3]+sum[2,3];
dp[3,4]=dp[3,3]+dp[4,4]+sum[4,4];
第三阶段:
三三合并可以拆成两两合并,拆分方法有两种,前两个为一组或后两个为一组
dp[1,3]=dp[1,2]+dp[3,3]+sum[1,3]或dp[1,3]=dp[1,1]+dp[2,3]+sum[1,3];取其最优
dp[2,4]=dp[2,2]+dp[3,4]+sun[2,4]或
dp[2,4]=dp[2,3]+dp[3,3]+sum[2,4];取其最优
第四阶段:
四四合并的拆分方法用三种,同理求出三种分法的得分,取其最优即可。以后第五阶段、第六阶段依次类推,最后在第六阶段中找出最优答案即可。
l和r的差距len=1
***********************************
l的位置l=1所以r的位置r=2
k的位置k=1
划分的区间[1,1] [2,2]
dp[1][2]=min(dp[1][2]=2147483647,dp[1][1]=0+dp[2][2]=0+sum=4)=4
l的位置l=2所以r的位置r=3
k的位置k=2
划分的区间[2,2] [3,3]
dp[2][3]=min(dp[2][3]=2147483647,dp[2][2]=0+dp[3][3]=0+sum=8)=8
l的位置l=3所以r的位置r=4
k的位置k=3
划分的区间[3,3] [4,4]
dp[3][4]=min(dp[3][4]=2147483647,dp[3][3]=0+dp[4][4]=0+sum=7)=7
l和r的差距len=2
***********************************
l的位置l=1所以r的位置r=3
k的位置k=1
划分的区间[1,1] [2,3]
dp[1][3]=min(dp[1][3]=2147483647,dp[1][1]=0+dp[2][3]=8+sum=9)=17
k的位置k=2
划分的区间[1,2] [3,3]
dp[1][3]=min(dp[1][3]=17,dp[1][2]=4+dp[3][3]=0+sum=9)=13
l的位置l=2所以r的位置r=4
k的位置k=2
划分的区间[2,2] [3,4]
dp[2][4]=min(dp[2][4]=2147483647,dp[2][2]=0+dp[3][4]=7+sum=10)=17
k的位置k=3
划分的区间[2,3] [4,4]
dp[2][4]=min(dp[2][4]=17,dp[2][3]=8+dp[4][4]=0+sum=10)=17
l和r的差距len=3
***********************************
l的位置l=1所以r的位置r=4
k的位置k=1
划分的区间[1,1] [2,4]
dp[1][4]=min(dp[1][4]=2147483647,dp[1][1]=0+dp[2][4]=17+sum=11)=28
k的位置k=2
划分的区间[1,2] [3,4]
dp[1][4]=min(dp[1][4]=28,dp[1][2]=4+dp[3][4]=7+sum=11)=22
k的位置k=3
划分的区间[1,3] [4,4]
dp[1][4]=min(dp[1][4]=22,dp[1][3]=13+dp[4][4]=0+sum=11)=22
从前往后计算--》小区间先大区间划分
//for (ll len = 1; len < n; len++) {//len代表l和r的下表的差值
// cout << "l和r的差距len=" << len << "\n***********************************\n";
// for (ll l = 1; l + len <= n; l++) {//从第l堆开始
// ll r = l + len;//到第r堆结束
// cout << "l的位置l=" << l <<"所以r的位置r="<<r<< "\n";
// dp[l][r] = INF;//求的是最小,先设其为INF
// for (ll k = l; k < r; k++) {//l和r之间用k分割
// cout << "k的位置k=" << k << "\n";
// cout << "划分的区间" << "[" << l << "," << k << "]\t" << "[" << k + 1 << "," << r << "]\n";
//
// cout << "dp[" << l << "]" << "[" << r << "]=" << "min(dp[" << l << "]" << "[" << r << "]=" <<dp[l][r] << "," << "dp[" << l << "]" << "[" << k << "]=" << dp[l][k] << "+dp[" << k + 1 << "]" << "[" << r << "]=" << dp[k + 1][r] << "+sum=" << prefix[r] - prefix[l - 1] << ")=";
// dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + prefix[r] - prefix[l - 1]);
// cout << dp[l][r] << "\n\n";
// }
// }
//}
动态方程为dp[l][r]=dp[l][k]+dp[k+1][r]+sum[l][r];
参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 1e3 + 5;
const ll MOD = 1e9 + 7;
const ll INF = 0x7fffffff;
ll n;
ll prefix[maxn];
ll dp[maxn][maxn];
int main() {
ios_base::sync_with_stdio(false), cin.tie(0);
cin >> n;
for (ll i = 1; i <= n; i++) {
ll t;
cin >> t;
prefix[i] = prefix[i - 1] + t;//求前缀和
}
for (ll len = 1; len < n; len++) {//len代表l和r的下表的差值
for (ll l = 1; l + len <= n; l++) {//从第l堆开始
ll r = l + len;//到第r堆结束
dp[l][r] = INF;//求的是最小,先设其为INF
for (ll k = l; k < r; k++) {//l和r之间用k分割
dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + prefix[r] - prefix[l - 1]);
}
}
}
cout << dp[1][n];
return 0;
}
题目二
题意:一个环中有n堆石子将其合并
输出其最小和最大价值
输入
4
4 5 9 4
输出
43
54
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 1e3 + 5;
const ll MOD = 1e9 + 7;
const ll INF = 0x7fffffff;
ll n;
ll w[maxn];
ll maxv[maxn][maxn],minv[maxn][maxn];
ll perfix[maxn];
int main() {
ios_base::sync_with_stdio(false), cin.tie(0);
ll n;
cin >> n;
for (ll i = 1; i <= n; i++) {
cin >> w[i];
w[n + i] = w[i];
}
for (ll i = 1; i <= n * 2; i++) perfix[i] = perfix[i - 1] + w[i];
for (ll len = 1; len < n; len++) {//最长的区间长度为n,差值取n-1
for (ll l = 1; l + len <= 2 * n; l++) {//区间起点l
ll r = l + len ;//则区间终点r实际取不到2*n,就可满足从首开始又取到首
minv[l][r] = INF;
for (ll k = l; k < r; k++) {
minv[l][r] = min(minv[l][r], minv[l][k] + minv[k + 1][r] + perfix[r] - perfix[l - 1]);
maxv[l][r] = max(maxv[l][r], maxv[l][k] + maxv[k + 1][r] + perfix[r] - perfix[l - 1]);
}
}
}
ll min_ans = INF;
ll max_ans = 0;
for (ll i = 1; i <= n; i++) {
min_ans = min(min_ans, minv[i][i + n-1]);
max_ans = max(max_ans, maxv[i][i + n-1]);
}
cout << min_ans << "\n" << max_ans;
return 0;
}