0
点赞
收藏
分享

微信扫一扫

区间DP问题

青青子衿谈育儿 2022-04-26 阅读 28

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;
}


 

举报

相关推荐

0 条评论