动态规划五

复健\(Day4\)

动态规划(五)区间\(DP\)

\(1.\)石子合并

https://www.acwing.com/problem/content/284/

这是区间\(DP\)的模板题

这道题看似和果子合并很相像,于是我们想到贪心,但是这是行不通的

因为我们每次只能合并相邻的两堆,可以在纸上画一下样例,显然是不对的

\(f[l][r]\)表示把从\(l\)\(r\)合并成一堆的最小代价:\(f[l,r]=f[l][k]+f[k+1][r]+s[r]-s[l-1]\)

边界:\(f[i][i]=0\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 310
#define inf 0x3f3f3f3f
using namespace std;

int f[maxn][maxn];
int s[maxn];
int a[maxn];

int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
	memset(f,0x3f,sizeof(f));
	for(int i=1;i<=n;i++) f[i][i]=0;
	for(int len=2;len<=n;len++)//先枚举长度,再枚举左端点,最后枚举中间分割点
	{
		for(int l=1;l+len-1<=n;l++)
		{
			int r=l+len-1;
			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]);
			}
		}
	}
	printf("%d\n",f[1][n]);
	return 0;
}

\(2.\)环形石子合并

https://www.luogu.com.cn/problem/P1880

展环为链即可

注意最后输出不是\(f[1][n]\)\(g[1][n]\),而是要枚举起点,取最大值和最小值

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 310
#define inf 0x3f3f3f3f
using namespace std;

int f[maxn][maxn];
int g[maxn][maxn];
int s[maxn];
int a[maxn];

int main()
{
	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++) s[i]=s[i-1]+a[i];
	memset(f,0x3f,sizeof(f));
	memset(g,-0x3f,sizeof(g));
	for(int i=1;i<=2*n;i++) f[i][i]=0,g[i][i]=0;
	for(int len=2;len<=n;len++)
	{
		for(int l=1;l+len-1<=2*n;l++)
		{
			int r=l+len-1;
			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 maxx=0,minv=inf;
	for(int i=1;i<=n;i++) minv=min(minv,f[i][i+n-1]),maxx=max(maxx,g[i][i+n-1]);
	printf("%d\n%d\n",minv,maxx);
	return 0;
}

\(3.\)能量项链

https://www.luogu.com.cn/problem/P1063

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 210
using namespace std;

int f[maxn][maxn];
int a[maxn];

signed main()
{
	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++) f[i][i]=0;
	for(int len=2;len<=n;len++)
	{
		for(int l=1;l+len-1<=2*n;l++)
		{
			int r=l+len-1;
			for(int k=l;k<r;k++)
			{
				f[l][r]=max(f[l][r],f[l][k]+f[k+1][r]+a[l]*a[k+1]*a[r+1]);
			}
		}
	}
	int maxx=0;
	for(int i=1;i<=n;i++) maxx=max(maxx,f[i][i+n-1]);
	printf("%d\n",maxx);
	return 0;
}

注意\(l\)的终止条件是\(l+len-1<=2\times n\),如果我写成\(l\leqslant 2\times n\),会可能让数组越界,而且时间复杂度也变高了

posted on 2023-08-03 14:50  dolires  阅读(14)  评论(0)    收藏  举报

导航