石子合并 NOI1995 区间dp
Posted 行码棋
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了石子合并 NOI1995 区间dp相关的知识,希望对你有一定的参考价值。
题目链接
题目是环形的石子合并,一个环形的合并石子问题。
思路:将环形的拆开成一条链。将原数组复制一遍追加到原数组的后面,生成新的长度为2n的数组。几乎所有的环形问题都可以用该种方法优化。
然后该问题就是线性的石子合并问题。
状态表示问题:
f [ i ] [ j ] f[i][j] f[i][j]代表区间 [ i , j ] [i,j] [i,j]内石子合并的最小值
g [ i ] [ j ] g[i][j] g[i][j]代表区间 [ i , j ] [i,j] [i,j]内石子合并的最大值
初始化时需要如果求最大值,需要初始化为负无穷
如果是求最小值,需要初始化为正无穷
要注意:区间长度为1时,需要的代价为0
#include<bits/stdc++.h>
using namespace std;
const int N = 210,inf = 0x3f3f3f3f;
int n;
int f[N][N],g[N][N];
int a[N],s[N];
int main()
{
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++) s[i] += s[i-1]+a[i];
for(int len=2;len<=n;len++)
{
for(int i=1;i+len-1<=2*n;i++)
{
int l = i,r = i+len-1;
f[l][r] = inf;
g[l][r] = -inf;
for(int k=l;k<r;k++)
{
f[l][r] = min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
g[l][r] = max(g[l][r],g[l][k]+g[k+1][r]+s[r]-s[l-1]);
}
}
}
int mn=inf,mx = -inf;
for(int i=1;i<=n;i++)
{
mn = min(mn,f[i][i+n-1]);
mx = max(mx,g[i][i+n-1]);
}
cout<<mn<<"\\n"<<mx<<'\\n';
return 0;
}
另外的一种代码写法:
#include<bits/stdc++.h>
using namespace std;
const int N = 210,inf = 0x3f3f3f3f;
int n;
int f[N][N],g[N][N];
int a[N],s[N];
int main()
{
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++) s[i] += s[i-1]+a[i];
memset(f,inf,sizeof f);
memset(g,-inf,sizeof g);
for(int len=1;len<=n;len++)//长度从1开始
{
for(int i=1;i+len-1<=2*n;i++)
{
int l = i,r = i+len-1;
if(len==1) f[l][r]=g[l][r]=0;//长度为1时,赋值为0
for(int k=l;k<r;k++)
{
f[l][r] = min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
g[l][r] = max(g[l][r],g[l][k]+g[k+1][r]+s[r]-s[l-1]);
}
}
}
int mn=inf,mx = -inf;
for(int i=1;i<=n;i++)
{
mn = min(mn,f[i][i+n-1]);
mx = max(mx,g[i][i+n-1]);
}
cout<<mn<<"\\n"<<mx<<'\\n';
return 0;
}
以上是关于石子合并 NOI1995 区间dp的主要内容,如果未能解决你的问题,请参考以下文章