区间 环形 DPP1880 [NOI1995] 石子合并
Posted 鱼竿钓鱼干
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了区间 环形 DPP1880 [NOI1995] 石子合并相关的知识,希望对你有一定的参考价值。
【区间DP】P1880 [NOI1995] 石子合并
题目
思路
典中典,不过初次写的时候还是写得漏洞百出,个人感觉记忆化越来越香了,处理边界轻松很多。
这题要和合并果子区分,合并果子是任意选两个来合并,直接优先队列维护最值即可,这题必须是相邻合并。
然后要注意这题说是圆形操场上的,所以是个环形DP,环形DP如何处理?
常见的处理方式是赋值粘贴弄成两倍,然后变成n个非环形问题即可,所有的都要开2倍别漏掉。
区间DP比较容易想,f[l,r]为l,r区间合并成一堆的花费。
然后枚举分隔点即可
状态转移显而易见:
f
(
l
,
r
)
=
f
(
l
,
k
)
+
f
(
r
,
k
)
+
s
u
m
[
r
]
−
s
u
m
[
l
−
1
]
f(l,r)=f(l,k)+f(r,k)+sum[r]-sum[l-1]
f(l,r)=f(l,k)+f(r,k)+sum[r]−sum[l−1]
记忆化求最值的时候有几个需要注意的点
int get_min(int l,int r)
{
if(f[l][r])return f[l][r];
if(l==r)return f[l][r]=0;
int tmp=1e9+7;//设置临时变量
for(int i=l;i<r;i++)
tmp=min(tmp,get_min(l,i)+get_min(i+1,r)+sum[r]-sum[l-1]);//不能在这里return了
return f[l][r]=tmp; //在这里return
}
代码
// Problem: P1880 [NOI1995] 石子合并
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1880
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// FishingRod
#include<bits/stdc++.h>
using namespace std;
#define endl "\\n"
typedef long long LL;
typedef pair<int,int> PII;
//#define MULINPUT
/*DATA & KEY
*/
int T;
const int N=205;
int n;
int a[2*N],f[N][N],g[N][N],sum[N];
int get_min(int l,int r)
{
if(f[l][r])return f[l][r];
if(l==r)return f[l][r]=0;
int tmp=1e9+7;
for(int i=l;i<r;i++)
tmp=min(tmp,get_min(l,i)+get_min(i+1,r)+sum[r]-sum[l-1]);
return f[l][r]=tmp;
}
int get_max(int l,int r)
{
if(g[l][r])return g[l][r];
if(l==r)return g[l][r]=0;
int tmp=0;
for(int i=l;i<r;i++)
tmp=max(tmp,get_max(l,i)+get_max(i+1,r)+sum[r]-sum[l-1]);
return g[l][r]=tmp;
}
void solve(int C)
{
//NEW DATA CLEAN
//NOTE!!!
int n;cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],a[i+n]=a[i];
for(int i=1;i<=2*n;i++)sum[i]=sum[i-1]+a[i];
get_min(1,2*n);get_max(1,2*n);
int ans1=1e9+7,ans2=0;
for(int i=1;i<=n;i++)
{
ans1=min(f[i][n+i-1],ans1);
ans2=max(g[i][n+i-1],ans2);
}
cout<<ans1<<endl<<ans2;
}
int main()
{
#ifdef MULINPUT
scanf("%d",&T);
for(int i=1;i<=T;i++)solve(i);
#else
solve(1);
#endif
return 0;
}
以上是关于区间 环形 DPP1880 [NOI1995] 石子合并的主要内容,如果未能解决你的问题,请参考以下文章