jzoj6287-扭动的树【区间dp】
生活随笔
收集整理的這篇文章主要介紹了
jzoj6287-扭动的树【区间dp】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
正題
題目大意
一顆二叉查找樹,以keyikey_ikeyi?為建值,以pip_ipi?為價值。然后一個節點的sumsumsum定義為這棵子樹的價值之和。
要求相鄰兩個節點不互質的情況下所有節點的最大sumsumsum值之和。
解題思路
二叉查找樹滿足中序遍歷的建值從小到大,所以我們考慮區間dpdpdp。
每次將兩個區間組成的二叉樹合并在一起。
我們預處理出哪些節點可以相鄰
設f0/1,i,jf_{0/1,i,j}f0/1,i,j?表示作為將作為左邊/右邊的子樹與上將上一個合并過來(根為i/ji/ji/j且沒有另一邊子樹)時的最大價值。
那么我們可以得出動態轉移方程
f0,i,j=f1,i,k,+f0,k+1,j(vi?1,k)f_{0,i,j}=f_{1,i,k},+f_{0,k+1,j}(v_{i-1,k})f0,i,j?=f1,i,k?,+f0,k+1,j?(vi?1,k?)
f1,i,j=f1,i,k,+f0,k+1,j(vk,j+1)f_{1,i,j}=f_{1,i,k},+f_{0,k+1,j}(v_{k,j+1})f1,i,j?=f1,i,k?,+f0,k+1,j?(vk,j+1?)
codecodecode
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const ll N=310; struct node{ll k,p; }a[N]; ll n,f[2][N][N],ans,s[N]; bool v[N][N]; bool cmp(node x,node y) {return x.k<y.k;} int main() {//freopen("tree.in","r",stdin);//freopen("tree.out","w",stdout);scanf("%lld",&n);for(ll i=1;i<=n;i++)scanf("%lld%lld",&a[i].k,&a[i].p);for(int i=1;i<=300;i++)for(int j=1;j<=300;j++)for(int k=0;k<2;k++)f[k][i][j]=-0x7fffffffffffffll;sort(a+1,a+1+n,cmp);ans=-1e18;for(ll i=1;i<=n;i++)for(ll j=1;j<=n;j++) v[i][j]=(__gcd(a[i].k,a[j].k)==1);for(ll i=1;i<=n;i++){s[i]=s[i-1]+a[i].p;if(i!=1&&!v[i][i-1]) f[0][i][i]=a[i].p;if(i!=n&&!v[i][i+1]) f[1][i][i]=a[i].p;}for(ll l=2;l<=n;l++){for(ll i=1;i<=n-l+1;i++){ll j=i+l-1;for(ll k=i;k<=j;k++){ll b;if(k==i) b=f[0][i+1][j]+(s[j]-s[i-1]);if(k==j) b=f[1][i][j-1]+(s[j]-s[i-1]);if(i<k&&j>k) b=f[1][i][k-1]+f[0][k+1][j]+(s[j]-s[i-1]);if(i!=1&&!v[k][i-1]) f[0][i][j]=max(f[0][i][j],b);if(i!=n&&!v[k][j+1]) f[1][i][j]=max(f[1][i][j],b);if(l==n) ans=max(ans,b);}}}if(ans<0) printf("-1");else printf("%lld",ans); }總結
以上是生活随笔為你收集整理的jzoj6287-扭动的树【区间dp】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国航 CA1291 航班烟雾探测出现假报
- 下一篇: jzoj6288-旋转子段【优雅的暴力】