算法笔记--区间dp
1.石子归并问题
dp[i][j]表示区间i到j合并所需的最小花费。
先求出小区间的最小花费,再转移到大的区间。
转移方程:dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j])
初始状态:dp[i][i]=0
模板:
for(int i=1;i<=n;i++)cin>>a[i],sum[i]=sum[i-1]+a[i] for(int l=2;l<=n;l++){ for(int i=1;i+l-1<=n;i++){ int j=i+l-1; dp[i][j]=INF; 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]); } }
2.括号匹配问题
求最大括号匹配数
dp[i][j]表示i到j区间的最大括号匹配数
先求出小区间的最大括号匹配数,再转移到大区间。
状态转移:
dp[i][j]=dp[i+1][j-1]+2(s[i]=='('&&s[j]==')'||s[i]=='['&&s[j]==']')
dp[i][j]=max(dp[i][k]+dp[k+1][j],dp[i][j])(i<=k<j)
初始状态:dp[i][j]=0
模板:
for(int len=2;len<=s.size();len++){ for(int i=0;i<s.size();i++){ int j=i+len-1; if(j<s.size()){ if(s[i]=='('&&s[j]==')'||s[i]=='['&&s[j]==']')dp[i][j]=dp[i+1][j-1]+2; for(int k=i;k<j;k++) dp[i][j]=max(dp[i][k]+dp[k+1][j],dp[i][j]); } } }
记录路径,括号补全
用pos[i][j]记录i到j这段区间从哪个位置断开所要消耗的括号最少,然后从断点分开,递归输出答案。
模板:
void dfs(int l,int r){ if(l>r)return ; if(l==r){ if(s[l]=='('||s[l]==')')putchar('('),putchar(')'); else putchar('['),putchar(']'); } else{ if(pos[l][r]==-1){ putchar(s[l]); dfs(l+1,r-1); putchar(s[r]); } else{ dfs(l,pos[l][r]); dfs(pos[l][r]+1,r); } } } for(int l=2;l<=len;l++){ for(int i=0;i+l-1<len;i++){ int j=i+l-1; if(s[i]=='('&&s[j]==')'||s[i]=='['&&s[j]==']')dp[i][j]=dp[i+1][j-1]+2,pos[i][j]=-1; for(int k=i;k<j;k++){ if(dp[i][k]+dp[k+1][j]>=dp[i][j]){ dp[i][j]=dp[i][k]+dp[k+1][j]; pos[i][j]=k; } } } } dfs(0,len-1);
参考博客:http://blog.csdn.net/y990041769/article/details/24194605