此题解以纪念我终于差不多大概搞懂区间dp了(插个存档点,到时候忘了再回来看看)。
P1880 [NOI1995] 石子合并 题解
在做这道题之前,可以看看 P1775 石子合并(弱化版)(一道题解帮你搞定两道题,多划算)。
P1775 石子合并(弱化版)
形式化的题面
一堆石头摆在你面前,让你把他们扔到一起,每次扔都要花你这两堆石头的重量的力气,让你找出最省力的方法。
思路
使用区间dp
接下来我们就要将他换成一个流程,再编写成代码。
再看图,是不是我们就可以想到,枚举所有合并的区间。
- 第一层循环从2到 n n n 枚举区间的长度 l e n len len。
- 第二层循环去枚举这个区间的左端点 i i i ,那么这个区间的右端点就是显而易见的 i + l e n i+len i+len 。
- 再枚举 l e n len len 长的区间枚举每个分割可能的位置 k k k ,使得 d p [ i ] [ j ] = d p [ i ] [ k ] + d p [ k + 1 ] [ j ] dp[i][j] = dp[i ][ k] + dp[k + 1][j] dp[i][j]=dp[i][k]+dp[k+1][j] ;所以我们的 k k k 从 i i i 开始枚举,到 j j j 结束 ( i < = k < j ) ( i <= k < j ) (i<=k<j)
- 最后注意一下循环的范围就好了
- 对于代价(体力),我们可以根据前缀和进行记录,在代码中,我用 s [ i ] s[i] s[i] 记录,那么区间 [ i ] [ j ] [i][j] [i][j] 的代价就是 s [ j ] − s [ i − 1 ] s[j]-s[i-1] s[j]−s[i−1]
根据上述思路写的代码
#include<bits/stdc++.h>
using namespace std;
int dp[310][310],s[310]={0},n,a[310];
int main()
{
std::ios::sync_with_stdio(false);
cin>>n;
memset(dp,0x3f,sizeof(dp));
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i];//前缀和
dp[i][i]=0;
}
for(int i=2;i<=n;i++)//模拟区间
{
for(int j=0;j<=n-i+1;j++)
{
int l=j+i-1;
for(int k=j;k<l;k++)
{
dp[j][l]=min(dp[j][l],dp[j][k]+dp[k+1][l]+s[l]-s[j-1]);
}
}
}
cout<<dp[1][n];
return 0;
}
P1880 [NOI1995] 石子合并
再看一下这道题,我们只需再加一个max就好了。
请注意,这道题有环,那我们该怎么处理呢?
- 可以通过复制一遍这个数组,环转链就行了,在这道题中,
4 9 5 4
就可以处理为4 9 5 4 4 9 5 4
就行了
代码
#include<bits/stdc++.h>
using namespace std;
int dpma[310][310]={0},dpmi[310][310]={0},s[310]={0},n,a[310],ma=0,mi=0x3f3f3f;
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i+n]=a[i];
s[i]=s[i-1]+a[i];
}
for(int i=n+1;i<=2*n;i++)//环状处理
{
s[i]=a[i]+s[i-1];//前缀和
}
for(int len=1;len<n;len++)//区间[i,j]的长度
{
for(int i=1;i<=2*n-len;i++)//枚举左端点
{
int j=i+len;//右端点显而易见
dpmi[i][j]=0x3f3f3f;//在枚举min的时候,我们要将这个mi放大,毕竟自然数中,没有比0小的数了。
for(int k=i;k<j;k++)
{
dpmi[i][j]=min(dpmi[i][j],dpmi[i][k]+dpmi[k+1][j]+s[j]-s[i-1]);
dpma[i][j]=max(dpma[i][j],dpma[i][k]+dpma[k+1][j]+s[j]-s[i-1]);
//cout<<"mi:"<<dpmi[i][j]<<" ma:"<<dpma[i][j]<<endl;
}
}
}
for(int i=1;i<=n;i++)
{
//我们要得到的是整个链的结果,所以当i为起点,那么len=n,结尾就是i+n-1
ma=max(ma,dpma[i][i+n-1]);//整个链
mi=min(mi,dpmi[i][i+n-1]);
}
cout<<mi<<endl<<ma;
return 0;
}
标签:洛谷,dpmi,int,题解,310,len,枚举,NOI1995,dp
From: https://blog.csdn.net/yhlz64/article/details/144168817