前言
动态规划是一种思想,在此记录一道入门题目
作者:mzq
题目出处:洛谷
链接:https://www.luogu.org/problem/P1040
参考书籍:《算法导论》
题目
理解
解决此题的方法有两种,一种是树形dp,一种是记忆化搜索 在此会给出两种
矩阵的方法参照图:
树形dp
//这里首先将分数填入到f矩阵中,root矩阵保存有按顺序排好的节点值
for (int i = 1; i <= n; i++)
{
scanf("%lld",&f[i][i]);
f[i][i - 1] = 1;
root[i][i] = i;
}
for (int len = 1; len < n; len++)
{
for (int i = 1; i + len <= n; i++)
{
int j = i + len;
//先看第一个例子,f[1][2]=f[1][1]+f[2][2],然后再去比较其他的几个节点,
//可以参照上图
f[i][j] = f[i + 1][j] + f[i][i];
root[i][j] = i;
//这里把其他的几个节点通通搞了一遍。
for (int k = i+1 ; k < j; k++)
{
if (f[i][j] < f[i][k - 1] * f[k + 1][j] + f[k][k])
{
f[i][j] = f[i][k - 1] * f[k + 1][j] + f[k][k];
root[i][j] = k;
}
}
}
}
记忆化搜索
int dfs(long long left, long long right)
{
/*
左子树空,就返回分数一,参照图可以知道,当left>right即i>j时,
节点为left=right时的左子树或者右子树,但是left==right时已经是树的叶节点了,
所以left>right一定为空节点
*/
if (left > right)return 1;
//记忆搜索
if (f[left][right])return f[left][right];
//这里就跟树形打炮差不多了
for (int k = left; k <= right; k++)
{
//枚举根节点作为起点
int t = dfs(left, k - 1)*dfs(k + 1, right) + f[k][k];
if (t > f[left][right])
{
f[left][right] = t;
root[left][right] = k;
}
}
return f[left][right];
}
代码
树形dp完整代码
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
//节点个数
long long n;
long long f[50][50], root[50][50];
//这里由于矩阵摆放的位置有点奇怪,具体要看算法导论第229-230页
void print(long long left,long long right)
{
if (left > right)
{
return;
}
printf("%lld ",root[left][right]);
print(left, root[left][right] - 1);
print(root[left][right] + 1, right);
}
int main()
{
scanf("%lld",&n);
for (int i = 1; i <= n; i++)
{
scanf("%lld",&f[i][i]);
f[i][i - 1] = 1;
root[i][i] = i;
}
for (int len = 1; len < n; len++)
{
for (int i = 1; i + len <= n; i++)
{
int j = i + len;
f[i][j] = f[i + 1][j] + f[i][i];
root[i][j] = i;
for (int k = i+1 ; k < j; k++)
{
if (f[i][j] < f[i][k - 1] * f[k + 1][j] + f[k][k])
{
f[i][j] = f[i][k - 1] * f[k + 1][j] + f[k][k];
root[i][j] = k;
}
}
}
}
printf("%lld\n",f[1][n]);
print(1, n);
system("pause");
return 0;
}
记忆化搜索完整代码
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
//节点个数
long long n;
long long f[50][50] = { 0 }, root[50][50] = { 0 };
//这里由于矩阵摆放的位置有点奇怪,具体要看算法导论第229-230页
void print(long long left, long long right)
{
if (left > right)
{
return;
}
printf("%lld ", root[left][right]);
print(left, root[left][right] - 1);
print(root[left][right] + 1, right);
}
int dfs(long long left, long long right)
{
//左子树空,就返回分数一
if (left > right)return 1;
//记忆搜索
if (f[left][right])return f[left][right];
for (int k = left; k <= right; k++)
{
//枚举根节点作为起点
int t = dfs(left, k - 1)*dfs(k + 1, right) + f[k][k];
if (t > f[left][right])
{
f[left][right] = t;
root[left][right] = k;
}
}
return f[left][right];
}
int main()
{
scanf("%lld", &n);
for (int i = 1; i <= n; i++)
{
scanf("%lld", &f[i][i]);
root[i][i] = i;
}
printf("%d\n", dfs(1, n));
print(1, n);
system("pause");
return 0;
}