GDOI2016模擬8.10火星菌

題目大意:
對於一棵滿二叉樹,葉節點依次編號0~(2^k)-1,父節點的兩顆子樹可以任意旋轉,這種方式生成的所有序列A2n1 中,求最小的Ans=W[Ai][Ai+1] (相鄰兩個葉節點取的編號爲B)

我們可以發現,若將從根節點向左走爲0,向右走爲1,到達葉節點後得到的序列看做2進制的話,可以得到葉節點的初始編號0~(2^k)-1,作圖後可以發現,相鄰兩個葉節點必然是從某一個公共祖先開始,一個走0,一個走1,假設我們現在想確定i這個位置的數,i-1爲之前的相鄰葉節點,那麼i必然是走1的那邊,而且這個1是其編號末尾的第一個1,這個可以用lowbit=i&-i得到。

那麼知道這個有什麼用呢?設i到lca(i,i-1)的路程爲len,則從lca走0所得到的編號塊與走1得到的編號塊,能選的數在2^(len-1)(這個就是lowbit)這位必然不同,而後面的位可以隨意,那麼我們就可以從i-1的狀態推到i的狀態,易證這是沒有後效性的。(可以yy一下)

那麼我們設,f[i][j]爲到i這個位置爲止,選了j的序列得到的最小ANS,那麼推到f[i+1][k],k的可取範圍爲:s=(j^lowbit)&~(lowbit-1),t=s+lowbit,[s,t)

貼代碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 1000
#define MAXN 1000000000
#include<cmath>
using namespace std;
int n,ans;
int f[N][N],w[N][N];
void init(){
    scanf("%d",&n);
    n=1<<n;
    for (int i=0;i<n;i++)
        for (int j=0;j<n;j++)
            scanf("%d",&w[i][j]);
}
void work(){
    static int s,t,lowbit;
    fill(f[1],f[n+1],MAXN);
    for (int i=1;i<n;i++)
        for (int j=0;j<n;j++){
            lowbit=i&-i;
            s=(j^lowbit)&~(lowbit-1);
            t=s+lowbit;
            for (int k=s;k<t;k++)
                f[i][k]=min(f[i][k],f[i-1][j]+w[j][k]);
        }
    ans=MAXN;
    for (int i=0;i<n;i++)
        ans=min(ans,f[n-1][i]);
}
void write(){
    printf("%d",ans);
}
int main(){
    init();
    work();
    write();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章