石子合并
- 有 n 堆石子排成一排,第 i 堆石子有 ai 颗,每次我们可以选择相邻的两堆石子合并,代价是两堆石子数目的和,现在我们要一直合并这些石子,使得最后只剩下一堆石子,问总代价最少是多少?
- 输入格式:第一行一个数字 n。接下来一行 n个整数 a1,a2,…,an。
- 输出格式:一行一个整数,表示答案。
- 数据规模对于所有数据,保证 1≤n,ai≤500。
//递归做法
#include<bits/stdc++.h>
using namespace std;
int n, a[501], s[501], f[501][501];
int solve(int l, int r){
if(f[l][r] != -1)
return f[l][r];
if(l == r)
return f[l][r] = 0;
int ans = 1 << 30;
for(int m = l; m < r; m++)
ans = min(ans, solve(l, m) + solve(m+1, r));
return f[l][r] = ans + s[r] - s[l-1];
//
}
int main(){
scanf("%d", &n);
memset(f, 255, sizeof(f));
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for(int i = 1; i <= n; i++)
s[i] = s[i - 1] + a[i];
printf("%d\n", solve(1 , n));
}
- 用动态规划的思路来考虑,现在我们要把所有区间[l, r]的最小代价都算出来;
- 最优子结构:为了计算合并区间[i, j]的最小代价,我们需要先计算合并所有满足i <= k < j 的区间[ i, k ], [k + 1, j ]的最小代价;
- 状态:用f[ i ][ j ]表示合并区间[ i ][ j ]的最小代价。
- 转移:f[ i ][ j ] = min f[ i ] [ k ] + f[k + 1][ j ] + ax;
- 如何保证再算一个题的解之前,实现计算了这个问题的所有子问题的解
- 需要把区间按照j - i排序,从小到大计算即可。
//dp做法,复杂度低
#include<bits/stdc++.h>
using namespace std;
int n, a[501], s[501], f[501][501];
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for(int i = 1; i <= n; i++)
s[i] = s[i-1] + a[i];
memset(f, 127, sizeof(f));
for(int i = 1; i<= n; i++)
f[i][i] = 0;
for(int i = 1; i < n; i++)
for(int j = 1; j <= n-i; j++)
for(int k = j; k < j+i; k++)
f[j][j+i] = min(f[j][j+i], f[j][k] + f[k+1][i+j] + s[j+i] - s[j-1]);
printf("%d\n", f[1][n]);
}
括号序列
-
给定一个长度为 n 的字符串 s,字符串由 (, ), [, ] 组成,问其中最长的合法子序列有多长?也就是说,我们要找到最大的 m,使得存在 i1,i2,…,im 满足 1≤i1<i2<⋯<im≤n 并且 si1si2…sim是一个合法的括号序列。
合法的括号序列的定义是:-
空串是一个合法的括号序列。
-
若 A 是一个合法的括号序列,则 (A), [A] 也是合法的括号序列。
-
若 A, B 都是合法的括号序列,则 AB 也是合法的括号序列。
-
-
输入格式:
第一行一个整数 n。接下来一行,一个长度为 n的字符串 s。 -
输出格式:
一个数,表示答案。- 状态:用f[ i ][ j ]表示最长合法子序列的长度;
- 转移:
- 如果si和sj匹配,f[ i ][ j ] = max(f[ i ][ j ], f[i + 1][j - 1]+2)
- 合并AB序列,枚举AB的分界线k, f[ i ][ j ] = max(f[ i ][ j ], f[ i ] [ k ] + f[k + 1][ j )
#include<bits/stdc++.h>
using namespace std;
int f[501][501], n;
char str[511];
int main(){
scanf("%d%s", &n, str + 1);
memset(f, 0, sizeof(f));
for(int i = 1; i < n; i++)
for(int j = 1; j <= n-i; j++){
if(str[j] == '(' && str[j+i] == ')' || str[j] == '[' && str[j+i] == ']')
f[j][j+i] = f[j+1][j+i-1] + 2;
for(int k = j; k < j+i; k++)
f[j][j+i] = max(f[j][j+i], f[j][k] + f[k+1][j+i]);
}
printf("%d", f[1][n]);
}