题解 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;
}
欢迎交流讨论,请点个赞哦~
本文来自博客园,作者:Maplisky,转载请注明原文链接:https://www.cnblogs.com/lbh2021/p/14931621.html

浙公网安备 33010602011771号