AcWing 1068. 环形石子合并
题意:有一排石子,每次将相邻的两堆石子合并,首尾也视作相邻的,求最大和最小的合并价值。
思路:将环形的转换为一条水平的链,就是把原来的值复制一份连接到后面就变成了长度为2n的一维数组,然后按“石子合并”来就行了。最后在计算答案的时候枚举右端点,找出长度为n的区间的最大和最小值就可。
#include<iostream>
#include<cstring>
using namespace std;
const int N=410,INF=0x3f3f3f3f;
int f[N][N],g[N][N];
int w[N],s[N];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>w[i];
w[i+n]=w[i];
}
for(int i=1;i<=2*n;i++)
s[i]=s[i-1]+w[i];
memset(f,-INF,sizeof(f));
memset(g,INF,sizeof(g));
for(int len=1;len<=n;len++)//枚举区间长度
for(int l=1;l+len-1<=2*n;l++)//枚举右端点
{
int r=l+len-1;
if(len==1) f[l][r]=g[l][r]=0;//自己这一堆不需要合并
for(int k=l;k<r;k++)//枚举分割点
{
f[l][r]=max(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
g[l][r]=min(g[l][r],g[l][k]+g[k+1][r]+s[r]-s[l-1]);
}
}
int maxv=-INF,minv=INF;
for(int i=1;i<=n;i++)//找出长度为n的区间最大和最小值
{
maxv=max(maxv,f[i][i+n-1]);
minv=min(minv,g[i][i+n-1]);
}
cout<<minv<<"\n"<<maxv<<"\n";
return 0;
}
320. 能量项链
题意:简化一下,有n个矩形,每个矩形是(i,j)i行j列,每次合并相邻矩形形成新的矩形,例如合并(2,3)+(3,4)→得到新的矩形(2,4),求最大值。注意首尾也算相邻。
思路:f[L][R]表示合并L~R区间里的所有珠子能得到的最大能量。
思路与上一题是相似的,只是在处理数据上,边界上要注意。
#include<iostream>
#include<cstring>
using namespace std;
const int N=210;
int f[N][N];
int w[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>w[i];
w[i+n]=w[i];
}
//memset(f,-0x3f,sizeof(f));//这里不需要初始化为无穷小,因为在未合并珠子之前,能量就是0
for(int len=3;len<=n+1;len++)//这里长度从3(两颗珠子)开始枚举,这样才能枚举分割点,还要枚举到n+1,才是枚举了n颗珠子
for(int l=1;l+len-1<=2*n;l++)
{
int r=l+len-1;//减1是因为我们枚举的是长度为len的区间,l~r包含了l了,所以要减1
for(int k=l+1;k<r;k++)//这里k不能等于l,这样的话f[L][L]它不是一颗完整的珠子,完整的珠子是有头有尾的
f[l][r]=max(f[l][r],f[l][k]+f[k][r]+w[l]*w[k]*w[r]);
}
int maxv=-0x3f;
for(int i=1;i<=n;i++)
maxv=max(maxv,f[i][i+n]);
cout<<maxv<<endl;
return 0;
}
1069. 凸多边形的划分
题意:将n个顶点的凸多边形划分成互不相交的三角形的最大价值。
思路:我们会发现当确定了一个三角形后,由于三角形不能相交,所以一整个凸边形刚好被这个确定的三角形划分为左右两部分,所以要求的值为:左边+右边+这个三角形三个顶点的权值乘积,即f[L,R]=f[L,K]+f[K,R]+w[L]*w[K]*w[R],K→(L,R)
f[L,R]]表示将(L,L+1),(L+1,L+2)...(L+K,R)这个凸多边形划分成不相交的三角形的所有方案。
①无高精度版
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=55,M=35,INF=0x3f3f3f3f;
int w[N];
ll f[N][N];
int n;
int main()
{
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;
f[l][r]=INF;
for(int k=l+1;k<r;k++)
f[l][r]=min(f[l][r],f[l][k]+f[k][r]+w[l]*w[k]*w[r]);
}
cout<<f[1][n]<<endl;//将[1,n]这一段凸多边形划分成三角形的最小值
return 0;
}
②高精度版
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=55,M=35,INF=0x3f3f3f3f;
int w[N];
ll f[N][N][M];//新加一维来存储高精度算出来的值
int n;
void add(ll a[],ll b[])
{
ll c[M];
for(int i=0,t=0;i<M;i++)
{
t+=a[i]+b[i];
c[i]=t%10;
t/=10;
}
memcpy(a,c,sizeof(c));
}
void mul(ll a[],ll b)
{
ll c[M];
ll t=0;
for(int i=0;i<M;i++)
{
t+=a[i]*b;
c[i]=t%10;
t/=10;
}
memcpy(a,c,sizeof(c));
}
bool cmp(ll a[],ll b[])
{
for(int i=M-1;i>=0;i--)
{
if(a[i]<b[i])
return true;
if(a[i]>b[i])
return false;
}
return false;
}
void print(ll a[])
{
int k=M-1;
while(k&&!a[k]) k--;
while(k>=0)
cout<<a[k--];
cout<<endl;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>w[i];
ll temp[M];
for(int len=3;len<=n;len++)
for(int l=1;l+len-1<=n;l++)
{
int r=l+len-1;
f[l][r][M-1]=1;//初始化为无穷大,因为是逆序存放的,所以将第M-1为置为1
for(int k=l+1;k<r;k++)
{
memset(temp,0,sizeof(temp));
temp[0]=w[l];//将原来试子加法,乘法换成高精度
mul(temp,w[k]);
mul(temp,w[r]);
add(temp,f[l][k]);
add(temp,f[k][r]);
if(cmp(temp,f[l][r]))
memcpy(f[l][r],temp,sizeof(temp));
}
}
print(f[1][n]);
return 0;
}