題目大意:
對於一棵滿二叉樹,葉節點依次編號0~(2^k)-1,父節點的兩顆子樹可以任意旋轉,這種方式生成的所有序列
我們可以發現,若將從根節點向左走爲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;
}