// 石子取数.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<algorithm>
#include<cstring>
#include<string>
//区间dp经典例题,如果能随机将两个石碓堆在一起的话,那就是哈弗曼树贪心问题了
//这题石头是排成一个环的,区间DP就是从一个个小区间推出一个大区间,当然视题目而定也有状态转移而来的区间.
using namespace std;
const int maxn = 300;
int stone[maxn];
int main()
{
int n;
cin >> n;
int sum[maxn];
int dp[maxn][maxn];//我们假设DP值为从i到j的值
//dp值这一题是从小区间到大区间,所以我们枚举长度同时通过长度枚举起点和中点,再通过小区间求大区间
//处理区间问题重要的是枚举起点和终点,而环起点是不固定的对此我们有两个方法来解决
//1.通过不断枚举新的起点,到最后通过比较来获得答案
/*2
ABCDE
_BCDEA
__CDEAB
___DEABC
____EABCD
很明显我们要求的序列全在这里了,而这些序列是可以拼在一起的变成ABCDEABCDE,若要求第二个序列我们只需要求dp[2][5]就可以
所以我们只要像处理列一样处理环就行,正常求出dp值
*/
for (int i = 1;i <= n;i++)
{
cin >> stone[i];
stone[i + n] = stone[i];
}
for (int i = 1;i <= 2*n;i++)
{
sum[i] = sum[i - 1] + stone[i];//我们处理一下前缀和
}
memset(dp, 0, sizeof(dp));
for (int length = 1;length < n;length++)
{
for (int i = 1, j = length + 1;j <= 2*n-1;i++, j++)//因为序列变长这里J边界也变大
{
dp[i][j] = 100000;
for (int s = i;s < j;s++)
{
//我们要看看从i到j到底哪一块小区间是最小的,当然要加上本身需要产生的代价
dp[i][j] = min(dp[i][j], dp[i][s] + dp[s + 1][j] + sum[j] - sum[i - 1]);//本体的状态转移方程
}
}
}
int ans = 100000;
for (int i = 1, j = n;j <= 2 * n - 1;j++, i++)
{
ans = min(ans, dp[i][j]);
}
cout << ans << endl;
memset(dp, 0, sizeof(dp));
for (int length = 1;length < n;length++)
{
for (int i = 1, j = length + 1;j <= 2*n-1;i++, j++)
{
dp[i][j] = 0;
for (int s = i;s < j;s++)
{
dp[i][j] = max(dp[i][j], dp[i][s] + dp[s + 1][j] + sum[j] - sum[i - 1]);
}
}
}
ans = 0;
for (int i = 1, j = n;j <= 2 * n - 1;j++, i++)
{
ans = max(ans, dp[i][j]);
}
cout << ans;
}
石头合并 DP区间
最新推荐文章于 2024-09-03 12:28:49 发布