区间DP
1.环形石子合并
题意 :
环状石子合并相邻的两堆,询问最大最小开销
思路 :
状态表示 :
f
[
l
]
[
r
]
f[l][r]
f[l][r]表示从
l
l
l开始到
r
r
r的花费
状态计算 :
f
[
l
]
[
r
]
=
m
i
n
/
m
a
x
(
f
[
l
]
[
r
]
,
f
[
l
]
[
k
]
+
f
[
k
]
[
r
]
+
s
[
r
]
−
s
[
l
−
1
]
)
f[l][r] = min/max(f[l][r],f[l][k]+f[k][r]+s[r]-s[l-1])
f[l][r]=min/max(f[l][r],f[l][k]+f[k][r]+s[r]−s[l−1])
因为是环形问题,我们考虑开两倍的空间用于存放
那么对于 i i i就对应 i + n − 1 i+n-1 i+n−1
code :
int n;
int w[N],s[N];
int f[N][N],g[N][N];
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>w[i];
w[i+n] = w[i];///两倍数组
}
for(int i=1;i<=n*2;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 l = 1;l+len-1<=n*2;l++)
{
int r = l+len-1;
if(l == r) g[l][r] = f[l][r] = 0;///如果区间长度是1
else
{
for(int k=1;k<r;k++)///状态转移i计算
{
g[l][r] = max(g[l][r],g[l][k]+g[k+1][r]+s[r]-s[l-1]);
f[l][r] = min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
}
}
}
}
int minv = INF ,maxv = -INF;
for(int i=1;i<=n;i++)
{
minv =min(minv,f[i][i+n-1]);
maxv =max(maxv,g[i][i+n-1]);
}
cout<<minv<<'\n'<<maxv<<'\n';
}
2.能量项链
题意 :
给定一个项链对于上面的珠子有两个值
r
,
t
r,t
r,t表示首和尾,你可以让两个相邻的珠子进行合并,合并产生的开销是
r
1
∗
t
∗
r
2
r_1*t*r_2
r1∗t∗r2,询问最后变为一个珠子的最小开销
思路 :
处理方法等同于环形石子合并,但是又有很多细节上的差异
因为最终合并的原因导致最后数组的长度是 n + 1 n+1 n+1, 最后答案输出需要是 f [ i ] [ i + 1 ] f[i][i+1] f[i][i+1]
code :
int n;
int w[N],s[N];
int f[N][N],g[N][N];
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>w[i];
w[i+n] = w[i];///两倍数组
}
memset(f,-0x3f,sizeof f);
for(int len = 2;len <= n+1; len ++ ){//最后长度可以达到n=1
for(int l=1;l+len-1<=n*2; l ++ ){
int r = l + len-1;
if(len == 2)f[l][r] = 0 ;
else
for(int k = l+1;k<r;k++){
f[l][r] = max(f[l][r],f[l][k]+f[k][r]+w[k]*w[r]*w[l]);
}
}
}
int res = 0 ;
for(int i=1;i<=n;i++)
res = max(res,f[i][i+n]);
cout<<res<<endl;
}
4.凸边形的划分
题意 :
给定一个
N
N
N个顶点的凸多边形,每个顶点都有一个权值,让你将这个多边形划分为
N
−
2
N-2
N−2个互不相交的三角形,对于每个三角形其顶点相乘可用获得一个权值,询问所有三角形权值乘积之和最少是多少
思路 :
我们假定选中一个三角形
又因为题目中给定的,互不相交,因此左右两边独立,所以这时候就很像区间dp了
状态表示 :
f
[
L
,
R
]
f[L,R]
f[L,R]表示 所有将
(
L
,
L
+
1
)
,
(
L
+
1
,
L
+
2
)
.
.
.
.
(
R
,
L
)
(L,L+1),(L+1,L+2)....(R,L)
(L,L+1),(L+1,L+2)....(R,L)这个多边形划分为三角形的最小方案
状态转移 :
f
[
L
,
R
]
=
f
[
L
,
k
]
+
f
[
k
,
R
]
+
w
[
L
]
∗
w
[
k
]
∗
w
[
R
]
f[L,R] =f[L,k]+f[k,R]+w[L]*w[k]*w[R]
f[L,R]=f[L,k]+f[k,R]+w[L]∗w[k]∗w[R]
同时有因为这个题需要使用高精度, y y y总在这里用了一个偷懒的办法
就是多开一维数组记录每个位置的数
code :
const int N = 55,INF = 0x3f3f3f3f , M = 35;//保守点取35位
const double eps = 1e-5;
struct node{
int to,val;
};
vector<int> f[N][N];
//f[l][r] 表示(l,l+1)...(l,r)划分成三角形所有方案的最小值
int w[N];//记录点权
int n;
vector<int> add(vector<int>& a,vector<int>& b){
int t = 0;
vector<int> c;
for(int i = 0;i < a.size() || i < b.size();++i){
if(i < a.size()) t += a[i];
if(i < b.size()) t += b[i];
c.push_back(t % 10);
t /= 10;
}
if(t) c.push_back(t);
return c;
}
vector<int> mul(vector<int>& a,LL b){
vector<int> c;
LL t = 0;
for(int i = 0;i < (int)a.size() || t;++i){
if(i < a.size()) t += a[i] * b;
c.push_back(t % 10);
t /= 10;
}
return c;
}
void reslove(vector<int>& t,int num){ //将num分解后加入到t中
while(num){
t.push_back(num % 10);
num /= 10;
}
if(num) t.push_back(num);
}
bool cmp(vector<int>& a,vector<int>& b)
{
if (a.size() != b.size()) return a.size() > b.size();
int i = (int)a.size() - 1;
while (a[i] == b[i] && i > 0) --i;
return a[i] > b[i];
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++) cin>>w[i];
//三条边才可以构建一个三角形
for(int len = 3;len<=n;len ++ ){
for(int l=1;l+len-1<=n;l ++ ){
int r = l+len-1;
//枚举区间的底
for(int i = 0;i<M; i ++ ){
f[l][r].pb(1);
}//初始化为正无穷
//枚举中间的分割点
for(int k = l+1 ;k<=r-1 ;k ++ ){
vector<int> temp;
reslove(temp,w[1]);
mul(temp,w[k]);
mul(temp,w[r]);
add(temp,f[l][k]);
add(temp,f[k][r]);
if(cmp(temp,f[l][r]))
f[l][r] = temp;
}
}
}
for(int i=n-1;i>=0;i--)
cout<<f[1][n][i];
}