题目描述
现在要添上n-1对括号,加法运算依括号顺序进行,得到n-1个中间和,求出使中间和之和最小的添括号方法。
输入数据
共两行。
第一行,为整数 nn 。 (1≤n≤20)
第二行,为 a (1),a (2),...,a (n) 这 n 个正整数,每个数字不超过100。输出数据
输出 3 行。
第一行,为添加括号的方法。
第二行,为最终的中间和之和。
第三行,为 n−1 个中间和,按照从里到外,从左到右的顺序输出。样例输入
4 4 1 2 3
样例输出
(4+((1+2)+3)) 19 3 6 10
解析:动态规划,dp[i][j] 表示 i 到 j 经划分后的最小中间和
更新方程:dp[i][j] = min(dp[i][j] , dp[i][k] + dp[k+1][j] + L[i][j]),其中 L[i][j] 表示 i 到 j 之间的中间和。
第一步:计算任意两点之间的中间和
for(int i=1; i<=n; i++) for(int j=i; j<=n; j++) L[i][j]=L[i][j-1]+a[j]; //中间和
第二步:依据考察区间的长度,遍历 i, 使用 vis[i][j] 记录中间插入的符号位置。
for(int len=1; len<n; len++){ //考察的区间长度 for(int i=1; i<=n; i++){ int j=i+len; if(j>n) break; dp[i][j]=INF; for(int k=i; k<j; k++) if(dp[i][j] >= dp[i][k]+dp[k+1][j]+L[i][j]){//!!!一定要加等号,原因见样例2 dp[i][j]=dp[i][k]+dp[k+1][j]+L[i][j]; vis[i][j]=k; } } }
第三步:递归输出结果
void Print( int l, int r ) { if(l==r) { cout<<L[l][r]; return; } cout<<"("; Print(l, vis[l][r]); cout<<"+"; Print(vis[l][r]+1, r); cout<<")"; a[++cnt]=L[l][r];//存储中间和 }
完整代码:
#include<iostream> using namespace std; const int INF=0x3f3f3f3f; const int N=25; int n, a[N], cnt; int dp[N][N], L[N][N]; int vis[N][N]; //记录路径 void Print( int l, int r ) { if(l==r) { cout<<L[l][r]; return; } cout<<"("; Print(l, vis[l][r]); cout<<"+"; Print(vis[l][r]+1, r); cout<<")"; a[++cnt]=L[l][r];//存储中间和 } int main() { cin>>n; for(int i=1; i<=n; i++) cin>>a[i]; for(int i=1; i<=n; i++) for(int j=i; j<=n; j++) L[i][j]=L[i][j-1]+a[j]; //前缀和 for(int len=1; len<n; len++){ //考察的区间长度 for(int i=1; i<=n; i++){ int j=i+len; if(j>n) break; dp[i][j]=INF; for(int k=i; k<j; k++) if(dp[i][j] >= dp[i][k]+dp[k+1][j]+L[i][j]){//!!!一定要加等号,原因见样例2 dp[i][j]=dp[i][k]+dp[k+1][j]+L[i][j]; vis[i][j]=k; } } } Print(1, n); cout<<endl; if(n!=1) cout<<dp[1][n]<<endl; else cout<<L[1][n]<<endl; for(int i=1; i<cnt; i++) { cout<<a[i]<<" "; } cout<<a[cnt]<<endl; return 0; }