題解 Loj10151 【分離與合體】
阿新 • • 發佈:2021-06-25
簡單寫一寫好了,其實挺板的。
題目涉及區間拆解和合並,顯然是一個區間 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}); } }
最後給出完整程式碼:
#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; }
歡迎交流討論,請點個贊哦~