从小白角度,详解各类解法,从暴力到终极优化,帮助新手快速入门!
目录
- 题目描述:
- 小白到进阶各种解法:
- 一、暴搜:
- 思路:
- 代码:
- 二、记忆化搜索:
- 思路:待更新!
- 代码:
- 三、本题考察算法:区间DP
- 代码:
将 n 堆石子绕圆形操场排放,现要将石子有序地合并成一堆。
规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。
请编写一个程序,读入堆数 n 及每堆的石子数,并进行如下计算:
选择一种合并石子的方案,使得做 n−1 次合并得分总和最大。
选择一种合并石子的方案,使得做 n−1 次合并得分总和最小。
输入格式
第一行包含整数 n,表示共有 n 堆石子。
第二行包含 n 个整数,分别表示每堆石子的数量。
输出格式
输出共两行:
第一行为合并得分总和最小值,
第二行为合并得分总和最大值。
数据范围
1 ≤ n ≤ 200
输入样例
4
4 5 9 4
输出样例
43
54
总结:递归;
递归的本质是一棵树!从树的角度思考如何设计递归!
节点
)的分支有哪些(枚举
)?即枚举所有的分支,如果分支多,采用循环,分支少采用顺序枚举就行了。一般都是循环枚举#include
#include
#include
using namespace std;
const int N = 3e2 + 10;
int a[N];
int s[N];
int n;
int dfs_min (int l, int r)
{
if(l == r)
return 0; //叶子节点!
int res=1e8;
for (int k=l; k < r; k ++)
res = min(res, dfs_min(l, k) + dfs_min(k+1, r) + s[r] - s[l-1]);
return res;
}
int dfs_max (int l, int r)
{
if (l==r)
return 0;
int res=0;
for (int k=l; k < r; k ++)
res = max(res, dfs_max (l, k) + dfs_max (k+1,r) + s[r] - s[l-1]);
return res;
}
int main()
{
cin >> n;
for (int i=1; i <= n; i ++)
cin >> a[i], a[i+n] = a[i];
for (int i=1; i <= 2*n; i ++)
s[i] = s[i-1] + a[i];
int ans1 = 0x3f3f3f3f, ans2 = -0x3f3f3f3f;
for (int i=1; i <= n; i ++)
{
ans1 = min(ans1, dfs_min(i, i+n-1));
ans2 = max(ans2, dfs_max(i, i+n-1));
}
cout << ans1 << endl << ans2 << endl;
return 0;
}
#include
#include
#include
using namespace std;
const int N = 3e2 + 10;
int a[N];
int s[N];
int n;
int f[N][N];
int g[N][N];
int dfs_min (int l, int r)
{
if (f[l][r]) return f[l][r];
if(l == r)
return 0; //叶子节点!
int res=1e8;
for (int k=l; k < r; k ++)
res = min(res, dfs_min(l, k) + dfs_min(k+1, r) + s[r] - s[l-1]);
return f[l][r] = res;
}
int dfs_max (int l, int r)
{
if (g[l][r]) return g[l][r];
if (l==r)
return 0;
int res=0;
for (int k=l; k < r; k ++)
res = max(res, dfs_max (l, k) + dfs_max (k+1,r) + s[r] - s[l-1]);
return g[l][r] = res;
}
int main()
{
cin >> n;
for (int i=1; i <= n; i ++)
cin >> a[i], a[i+n] = a[i];
for (int i=1; i <= 2*n; i ++)
s[i] = s[i-1] + a[i];
int ans1 = 0x3f3f3f3f, ans2 = -0x3f3f3f3f;
for (int i=1; i <= n; i ++)
{
ans1 = min(ans1, dfs_min(i, i+n-1));
ans2 = max(ans2, dfs_max(i, i+n-1));
}
cout << ans1 << endl << ans2 << endl;
return 0;
}
本题的递推方程其实和线性的石子合并问题是一样的。
只需要解释通透为什么会将环形拆分成 2 ∗ n 2*n 2∗n 的线性空间!
/*
本题不仅要考虑 切割区间的方式,还要枚举所有的区间。
哪个区间,哪种切割方式,所花费的代价最小,代价最高!
比如区间有:
[1,2,3,4]
[2,3,4,1]
[3,4,1,2]
[4,1,2,3] 4个数4个区间,n个数n个区间
200个数200个区间,
*/
#include
#include
#include
using namespace std;
const int N = 5e2;
int f[N][N];
int g[N][N];
int w[N];
int s[N];
int main()
{
int n;
cin >> n;
for (int i=1; i <= n; i ++)
cin >> w[i], w[n+i] = w[i];
for (int i=1; i <= 2*n; i ++)
s[i] = s[i-1] + w[i];
memset (f, -0x3f, sizeof (f));
memset (g, 0x3f, sizeof (g));
for (int len=1; len <= n; len ++) //枚举的所有的区间,现在的区间长度是增大了的!
{
for (int i=1; i+len-1 <= 2*n; i ++)
{
int j = i + len - 1;
if (len == 1){
f[i][j] = 0;
g[i][j] = 0;
continue;
}
for (int k=i; k <= j; k ++)
{
f[i][j] = max(f[i][j], f[i][k] + f[k+1][j] + s[j] - s[i-1]);
g[i][j] = min(g[i][j], g[i][k] + g[k+1][j] + s[j] - s[i-1]);
}
}
}
//从所有区间里面选出最大代价的区间来!
int maxv = -0x3f3f3f3f;
int minv = 0x3f3f3f3f;
for (int i=1; i <= n; i ++)
{
maxv = max(maxv, f[i][n+i-1]);
minv = min(minv, g[i][n+i-1]);
}
cout << minv << endl;
cout << maxv << endl;
return 0;
}