uvalive 6669 hidden tree(好壯壓dp)
阿新 • • 發佈:2017-06-12
sum == com 不能 枚舉 http tar namespace ont
題目見here
題意:給一個序列arr[],你從中選擇一些子序列,將子序列的值從左往右依次放到某棵二叉樹的葉子節點上,使得除了葉子,全部節點左右子樹權和相等。子樹的權和 = 子樹葉子的權和。
假設存在這樣一棵二叉樹,選擇的子序列就是合法的。問,最長的合法子序列是多少。
思路:
枚舉二叉樹可能的葉子的最小權(入手點)。顯然,能和此數一起組成二叉樹的數,要麽和這個數相等。要麽是這個數的2^k倍。把滿足這樣的關系的數。認做一個集合,顯然集合外的數,不能和集合內的數組成二叉樹。那麽,我們僅僅須要一個一個得求出全部集合的最長子序列就可以。
把集合內的所有數所有除以最小權。剩下的數為1,2,4,8,16,32,64.....這樣的2^k的數。如果你從左到右。第一個填的數為16,第二個填的數一定不會比16大,不然那個16無法合並。如果填的就是16,那就合成為32。當然,填小於16的數也是行的。
那麽,對於2^k的數。每一個數,在合並過程中一定僅僅有兩種狀態。有1個,或者沒有。
那麽我們似乎就能夠用狀態壓縮就可。
具體見代碼:
#include<algorithm> #include<iostream> #include<string.h> #include<stdio.h> using namespace std; const int INF=0x3f3f3f3f; const int maxn=1010; typedef long long ll; bool base[maxn*505],vis[505],iss[maxn]; int arr[maxn],dp[maxn*505],brr[maxn],sum[maxn]; int solve(int x,int n) { int i,j,lim,tp,ct=0,ans=0; for(i=1;i<=n;i++) if(arr[i]%x==0&&base[arr[i]/x]) brr[++ct]=arr[i]/x; for(i=1;i<=ct;i++) sum[i]=sum[i-1]+brr[i]; memset(dp,0xcf,sizeof dp); dp[0]=0; for(i=1;i<=ct;i++) { lim=2*brr[i]; for(j=sum[i];j>=lim;j--) { tp=j-brr[i]; if(!(tp&(brr[i]-1))) dp[j]=max(dp[j],dp[tp]+1); } dp[brr[i]]=max(dp[brr[i]],1); } for(i=1;i<=sum[ct];i++) if(base[i]) ans=max(ans,dp[i]); return ans; } int main() { int n,i,j,lim,ans; lim=500*maxn; for(i=1;i<lim;i<<=1) base[i]=true; while(scanf("%d",&n),n) { for(i=1;i<=n;i++) scanf("%d",&arr[i]); memset(vis,0,sizeof vis); for(i=1;i<=n;i++) { iss[i]=true;//是否葉子結點最小權值 if(vis[arr[i]]) { iss[i]=false; continue; } vis[arr[i]]=true; for(j=1;j<=n;j++) { if(j==i) continue; if(arr[i]!=arr[j]&&arr[i]%arr[j]==0&&base[arr[i]/arr[j]]) { iss[i]=false; break; } } } ans=0; for(i=1;i<=n;i++) if(iss[i]) ans=max(ans,solve(arr[i],n)); printf("%d\n",ans); } return 0; }
uvalive 6669 hidden tree(好壯壓dp)