题意: 有若干堆石子围成一圈儿, 每合并两堆石子, 就对答案贡献了这两堆石子的重量, 现询问答案的最大值与最小值.
>> face <<
Strategy:记忆化搜索或者区间dp(可用四边形不等式优化(日后补坑))
状态: d p m i n [ l ] [ r ] → dpmin[l][r]\to dpmin[l][r]→该区间内的最小收益, d p m a x [ l ] [ r ] → dpmax[l][r]\to dpmax[l][r]→该区间内最大收益
目标: d p m i n [ 1 ] [ n ] & d p m a x [ 1 ] [ n ] dpmin[1][n] \& dpmax[1][n] dpmin[1][n]&dpmax[1][n]
边界: 第一次转移的贡献全由石堆提供. 不需要额外提供边界(记忆化搜索),针对区间最小,首先要memset(dpmin, ∞ \infty ∞, sizeof(dpmin)), 然后确保 d p m i n [ i ] [ i ] = 0 , i ∈ [ 1 , 2 ∗ n ] dpmin[i][i] = 0,i\in[1, 2*n] dpmin[i][i]=0,i∈[1,2∗n]
合法判断: 本题无
转移方程: 区间dp, 枚举未知, 让所有能转移到该未知的状态来转移
{ d p m i n [ l ] [ r ] = m i n ( d p m i n [ l ] [ r ] , d p m i n [ l ] [ k ] + d p m i n [ k + 1 ] [ r ] ) + ∑ i = l i ≤ r a [ i ] ; d p m a x [ l ] [ r ] = m a x ( d p m a x [ l ] [ r ] , d p m a x [ l ] [ k ] + d p m a x [ k + 1 ] [ r ] ) + ∑ i = l i ≤ r a [ i ] ; \begin{dcases} dpmin[l][r] = min(dpmin[l][r], dpmin[l][k] + dpmin[k+1][r]) + \sum_{i = l}^{i \leq r} a[i];\\[2ex] dpmax[l][r] = max(dpmax[l][r], dpmax[l][k] + dpmax[k+1][r]) + \sum_{i = l}^{i \leq r} a[i]; \end{dcases} ⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧dpmin[l][r]=min(dpmin[l][r],dpmin[l][k]+dpmin[k+1][r])+i=l∑i≤ra[i];dpmax[l][r]=max(dpmax[l][r],dpmax[l][k]+dpmax[k+1][r])+i=l∑i≤ra[i];
attention: 圆环链化 (化为长度为2*n的链)
双倍经验:
- 循环由外到内应遵循: 阶段, 状态, 决策
- 区间dp的阶段大部分是区间长度, 这样可保证所有的子问题均被计算过
- 易错点: 左右下标的边界以及区间长度的转换
@author: jasonleft -- 记忆化
#include <bits/stdc++.h>
#include <bits/extc++.h>
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rev(i, a, b) for (int i = (a); i >= (b); --i)
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rof(i, a, b) for (int i = (a); i > (b); --i)
#define ll long long
#define db double
#define oo 0x3f3f3f3f
#define eps 0.00001
#define all(x) x.begin(), x.end()
#define met(a, b) memset(a, b, sizeof(a))
#define what_is(x) cerr << #x << " is " << x << endl
#define lowbit(x) x &(-x)
using namespace std;
const int maxn = 2e2 + 5;
const int mod = 9999973;
ll a[maxn], n, dp[maxn][maxn], ans, sum[maxn];
int dfs(int l, int r, bool mi)
{
if (r < l)
return 0;
if (dp[l][r])
return dp[l][r];
if (l == r)//小心
return dp[l][r] = 0;
if (mi)
{
int res = oo;
_rep(k, l, r - 1)//枚举能转移到[l,r]的状态
{
res = min(dfs(l, k, mi) + dfs(k + 1, r, mi), res);
}
return dp[l][r] = res + sum[r] - sum[l - 1];
}
else
{
int res = 0;
_rep(k, l, r - 1)
{
res = max(res, dfs(l, k, mi) + dfs(k + 1, r, mi));
}
return dp[l][r] = res + sum[r] - sum[l-1];
}
}
int main()
{
ios::sync_with_stdio(0);
cin >> n;
_rep(i, 1, n)
{
cin >> a[i];
a[i + n] = a[i]; //环可以转化为2*n的链
}
_rep(i, 1, n * 2)
{
sum[i] = sum[i - 1] + a[i];
}
ans = oo;
dfs(1, 2 * n, 1);
_rep(i, 1, n)
{
//what_is(dp[i][i + 1])
ans = min(ans, dp[i][i + n - 1]);
}
cout << ans << endl;
met(dp, 0);
dfs(1, 2 * n, 0);
ans = 0;
_rep(i, 1, n)
{
ans = max(ans, dp[i][i + n - 1]);
}
cout << ans << endl;
}
@author: jasonleft -- 区间dp
#include <bits/stdc++.h>
#include <bits/extc++.h>
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rev(i, a, b) for (int i = (a); i >= (b); --i)
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rof(i, a, b) for (int i = (a); i > (b); --i)
#define ll long long
#define db double
#define oo 0x3f3f3f3f
#define eps 0.00001
#define all(x) x.begin(), x.end()
#define met(a, b) memset(a, b, sizeof(a))
#define what_is(x) cerr << #x << " is " << x << endl
#define lowbit(x) x &(-x)
using namespace std;
const int maxn = 2e2 + 5;
const int mod = 9999973;
ll a[maxn], n, dpmin[maxn][maxn], dpmax[maxn][maxn], ans, sum[maxn];
int main()
{
ios::sync_with_stdio(0);
cin >> n;
_rep(i, 1, n)
{
cin >> a[i];
a[i + n] = a[i];
}
_rep(i, 1, 2 * n)
{
sum[i] = sum[i - 1] + a[i];
}
//边界相关
met(dpmin, oo);
_rep(i, 1, 2 * n)
{
dpmin[i][i] = 0;
}
//转移相关
_rep(len, 2, n)
{
_rep(l, 1, n * 2 - len + 1)
{
int r = l + len - 1;
_rep(k, l, r - 1)
{
dpmin[l][r] = min(dpmin[l][k] + dpmin[k + 1][r], dpmin[l][r]);
//what_is(dpmin[l][r]), what_is( sum[r] - sum[l - 1]);
dpmax[l][r] = max(dpmax[l][k] + dpmax[k + 1][r], dpmax[l][r]);
//what_is(dpmax[l][r]);
}
dpmin[l][r]+=sum[r]-sum[l-1];
dpmax[l][r]+=sum[r]-sum[l-1];
}
}
ll minans = oo, maxans = 0;
_rep(i, 1, n)
{
minans = min(dpmin[i][i + n - 1], minans);
maxans = max(dpmax[i][i + n - 1], maxans);
//what_is(dpmax[i][i + n - 1]);
}
cout << minans << "\n"
<< maxans << endl;
}
第一次回顾
- 精髓是转移, 易错点是边界, 还有答案的区间
- 链化是个好东西
#include <bits/stdc++.h>
#include <bits/extc++.h>
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rev(i, a, b) for (int i = (a); i >= (b); --i)
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rof(i, a, b) for (int i = (a); i > (b); --i)
#define ll long long
#define db double
#define oo 0x3f3f3f3f
#define eps 0.00001
#define all(x) x.begin(), x.end()
#define met(a, b) memset(a, b, sizeof(a))
#define id(x) ((x + 8))
#define what_is(x) cerr << #x << " is " << x << endl
#define lowbit(x) x &(-x)
using namespace std;
const int maxn = 2e3 + 9;
const int mod = 1e6 + 3;
int dpmin[maxn][maxn], n, a[maxn], dpmax[maxn][maxn], sum[maxn];
int main()
{
ios::sync_with_stdio(0);
int n;
cin >> n;
met(dpmin, oo);
_rep(i, 1, n)
{
cin >> a[i];
a[i + n] = a[i];
dpmin[i][i] = dpmin[i + n][i + n] = 0;
}
_rep(i, 1, 2*n){
sum[i] = sum[i-1] + a[i];
}
_rep(len, 2, n)
{
_rep(l, 1, 2 * n - len + 1)
{
int r = l + len - 1;
_rep(k, l, r-1){
dpmax[l][r] = max(dpmax[l][k] + dpmax[k+1][r] + sum[r] - sum[l-1], dpmax[l][r]);
dpmin[l][r] = min(dpmin[l][k] + dpmin[k+1][r] + sum[r] - sum[l-1], dpmin[l][r]);
}
}
}
int min_val = oo, max_val = 0;
_rep(i, 1, n)
{
min_val = min(dpmin[i][i + n - 1], min_val);
max_val = max(max_val, dpmax[i][i + n - 1]);
}
cout << min_val << endl
<< max_val << endl;
}