题解 Loj10151 【分离与合体】

题目传送门

简单写一写好了,其实挺板的。

题目涉及区间拆解和合并,显然是一个区间 Dp。每次将一个区间分成左右两部分,并加上额外的收益。于是得出方程:

\[dp_{l,r}=\max\{(a_l+a_r)\times a_j+dp_{l,j}+dp_{j+1,r}\} \]

解释一下:

\((a_l+a_r)*a_j\):这一部分就是拆解区间额外的收益。

\(dp_{l,j}+dp_{j+1,r}\):分成左右两个小区间,递归处理。然后由小区间组合得出大区间的答案。

这一部分直接搜索即可。

int dfs(int l,int r){
	if(l>r)return 0;
	if(~dp[l][r])return dp[l][r];//记忆化 
	dp[l][r]=0;
	F(j,l,r-1){
		int tmp=a[j]*(a[l]+a[r])+dfs(l,j)+dfs(j+1,r);
		
		if(tmp>dp[l][r])dp[l][r]=tmp,rec[l][r]=j;
	}
	return dp[l][r];
}

然后恶心的地方在于输出答案。

先打印一分为二的区域,然后从左到右打印二分为四的分离区域,然后是四分为八的……

观察这段话,发现实际上类似于 bfs,先输出同层次的答案。于是用队列模拟 bfs 即可(其实不恶心)

void output(){
	queue<P> q;
	q.push({1,n});
	while(!q.empty()){
		P x=q.front();
		q.pop();
		if(x.l==x.r)continue;
		printf("%d ",rec[x.l][x.r]);
		q.push({x.l,rec[x.l][x.r]});
		q.push({rec[x.l][x.r]+1,x.r});
	}
}

code:

#include<bits/stdc++.h>
#define reg register
#define F(i,a,b) for(reg int i=a;i<=b;++i)
using namespace std;
inline int read();
const int N=305;
int n,a[N],dp[N][N],rec[N][N];
struct P{
	int l,r;
};
int dfs(int l,int r){
	if(l>r)return 0;
	if(~dp[l][r])return dp[l][r];//记忆化 
	dp[l][r]=0;
	F(j,l,r-1){
		int tmp=a[j]*(a[l]+a[r])+dfs(l,j)+dfs(j+1,r);
		
		if(tmp>dp[l][r])dp[l][r]=tmp,rec[l][r]=j;
	}
	return dp[l][r];
}
void output(){
	queue<P> q;
	q.push({1,n});
	while(!q.empty()){
		P x=q.front();
		q.pop();
		if(x.l==x.r)continue;
		printf("%d ",rec[x.l][x.r]);
		q.push({x.l,rec[x.l][x.r]});
		q.push({rec[x.l][x.r]+1,x.r});
	}
}
int main(){
	memset(dp,-1,sizeof(dp));
	n=read();
	F(i,1,n)a[i]=read();
	printf("%d\n",dfs(1,n));
	output();	
	return 0;
}
inline int read(){
	reg int x=0;
	reg char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

AC

欢迎交流讨论,请点个赞哦~

posted @ 2021-06-25 16:47  Maplisky  阅读(93)  评论(0)    收藏  举报