石子合并
有n堆石子围成一个圈,每一堆石子可以和相邻的一堆合并得到新的一堆,所得的分数是新的一堆的数量。
所有的石子合并成一堆后,最大的分数,最小的分数是多少。
思路:
这是一个环,需要拆分成链,把这n堆,复制一遍,变成2n堆。
dp的时候还是得保证长度为n。dp_min[i][j]表示合并i~j这个区间所得到的最小的分数。
状态转移:dp_min[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + a[j] - a[i-1])
a数组是前缀和
#pragma warning(disable:4996)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 505;
const ll mod = 10000;
int a[205], dp_min[205][205], dp_max[205][205];
int main()
{
int n, i, j, MAX, MIN;
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
a[i + n] = a[i];
a[i] += a[i - 1];//这两句不能交换顺序,注意a[i]的值的含义
}
for (i = n+1; i <= 2*n; i++)
{
a[i] += a[i - 1];//后面的n个数的前缀和
}
for (int l = 2; l <= n; l++)//长度的取值最多为n
{
for (i = 1; (i + l - 1) < (n +n); i++)//j最大可以取到2n
{
j = i + l - 1;
dp_min[i][j] = 0x3f3f3f;//设为最大的值
dp_max[i][j] = -0x3f3f3f;//设为最小的值
for (int k = i; k < j; k++)
{
dp_min[i][j] = min(dp_min[i][j], dp_min[i][k] + dp_min[k + 1][j] + a[j] - a[i - 1]);
//状态转移,加上i~j这个区间的所有的数
dp_max[i][j] = max(dp_max[i][j], dp_max[i][k] + dp_max[k + 1][j] + a[j] - a[i - 1]);
}
}
}
MAX = -0x3f3f3f;
MIN = 0x3f3f3f;
for (i = 1; i <= n; i++)
{
MAX = max(MAX, dp_max[i][i + n-1]);
MIN = min(MIN, dp_min[i][i + n-1]);//保证是长度为n的区间
}
printf("%d\n%d\n", MIN, MAX);
return 0;
}
矩阵链乘法
计算n个矩阵相乘变成1个矩阵,最少的运算次数。
比如一个n1 X m和一个 m X n2 的矩阵相乘,计算的次数就是n1 * m * n2
输入是n+1个数,第i个矩阵用第i个数和第i+1个数来表示维度,
思路:
区间dp,区间长度最小为2,最大为n。
dp[i][j]表示i~j这个区间内,全部乘起来,需要的最少的乘法运算次数。
状态转移:
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + a[i-1]*a[k] * a[j])
#pragma warning(disable:4996)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<set>//没用的头文件太多,删去了一些,不然显得太长,
//无法编译的话,就再加上头文件吧
#define SITE set<node>::iterator
using namespace std;
typedef long long ll;
const int inf = 1e9 + 10;
const int mod = 1000007;
const int maxn = 1e7;
ll n;
ll p[1005], m[1005][1005], s[1005][1005];
int main()
{
ll i, j, k;
cin >> n;
for (i = 0; i <= n; i++)
{
cin >> p[i];//从0开始的n+1个数
}
for (ll l = 2; l <= n; l++)
{
for (i = 1; i <= n - l + 1; i++)
{
j = i + l - 1;
m[i][j] = 0x3f3f3f3f3f3f;//设置为最大值
s[i][j] = i;
for (k = i; k < j; k++)
{
ll temp = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
//状态转移
if (temp < m[i][j])
{
m[i][j] = temp;
s[i][j] = k;//k是分割点
}
}
}
}
cout << m[1][n] << endl;
return 0;
}