题目大意:
对数组进行任意次操作:选择相邻且相等的数,合并成一个比原来大1的数
问:数组的最小长度是多少?
思路:
区间DP
用dp[1][n]来记录最终的答案
经典的转移方程:dp[i][r]=min(dp[i][r],dp[i][k]+dp[k+1][r]),但没考虑如何合并.
怎样考虑合并呢?
从最原始的数组上观察,若[i,i],[r,r]区间长度为1,并且[i,i],[r,r]的值相等且相邻,那么就可以对[i,r]区间进行合并,合并后得到一种新状态。
怎样记录并表示新状态呢? 引入俩数组:
1.dp[i][j] 表示区间[i,j]的长度
2.w[i][j] 表示区间[i,j]的数值:其值只有在区间合并时才能被修改。
这样的话,新状态就可以表示为:
1.dp[i][r]=1;//合并完后 [i,r]成为1个数,表示[i,r]这个区间的状态可能可以用于下次合并。
2.w[i][r]=w[i]i+1;
格局一升,上升到普遍状态:
dp[i,k],dp[k+1,r] 的长度为1,并且w[i,k],w[k+1,r]的值相等,进行合并即可:
dp[i][r]=1;
w[i][r]=w[i][k]+1;((((或w[k+1][r]+1.
Code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,j,n) for(int i=j;i<=n;i++)
const int N=1e3+9;
int aa[N],dp[N][N],w[N][N];
int main() {
int n;
cin>>n;
rep(i,1,n)
cin>>w[i][i],dp[i][i]=1;
rep(i,1,n) {
rep(k,1,n) {
dp[i][k] = k-i+1;//记录区间长度
}
}
rep(len,1,n) { //区间长度
for(int i=1; i+len<=n; i++) { //区间起点
int r=i+len;//终点
rep(k,i,r-1) {//间断点
dp[i][r]=min(dp[i][r],dp[i][k]+dp[k+1][r]);
if(dp[i][k]==1&&dp[k+1][r]==1&&w[i][k]==w[k+1][r]) {
dp[i][r]=1;
w[i][r]=w[i][k]+1;
}
}
}
}
cout<<dp[1][n];
return 0;
}