斯坦納樹就是在給定邊集中找最短網路(給定點+擴充套件點)使給定點聯通。
包含給定k個節點的最小生成樹,一般k很小,在網格圖上。
f[i][s]表示以點i為根,已和s集合裡的點聯通的生成樹的最小代價。
1. i的不同子樹的合併,f[i][s]=min;
2. 考慮新增給定子集外的點的情況,f[i][s]=f[j][s]+a[i][j];
即:從一棵合法的樹乙個點乙個點向外擴張;所求路徑是一顆斯坦納樹。點數k ≤ 10,所以可以用2進製狀壓來表示點的連線狀態。比如這棵樹,褐色是給定點集,下面兩個褐色點聯通後從中間的紅點向上擴張,
在最上面的褐點的位置再用第一種轉移合併即可得到以最上面的褐點為根的這棵樹。
定義 f[x][y][o] 表示以(x, y)為根的樹滿足二進位制狀態o時需要的最小代價。對於式<2>,可以想到最短路中的dist陣列更新,即:spfa維護 dist[u1] <= dist[u2] + w[u1][u2] 。<1> f[x][y][o] = f[x][y][o1] + f[x][y][o2] - a[x][y] (o = o1 | o2)
<2> f[x][y][o] = f[p][q][o] + a[x][y] (點(x, y)與 點(p, q)相鄰)
在維護了<1>後,f[x][y][o]不為inf的點,可以先入spfa的佇列,以便更新其他點。
1. 依次列舉狀態o; 2. 對於某一狀態, 列舉點(x, y),用 <1> 維護更新;
3. f[x][y][o]不為inf的點入隊; 4. spfa 維護 <2> ; 5. 維護更新狀態為o的情況。
答案輸出問題只需要在轉移時記下前驅, 在輸出前遞迴標記最優路徑即可。
#include#include#include
#include
#include
#include
#include
#include
#include
using
namespace
std;
typedef
long
long
ll;//
【p4294】遊覽計畫
//[斯坦納樹]
//f[x][y][o] 即以(x, y)為根的樹滿足二進位制狀態o時需要的最小代價。
//<1> f[x][y][o] = f[x][y][o1] + f[x][y][o2] - a[x][y] (o = o1 | o2)
//<2> f[x][y][o] = f[p][q][o] + a[x][y] (點(x, y)與 點(p, q)相鄰)
void reads(int &x)
while(s>='
0'&&s<='9')
x*=fx; //
正負號}
int n,m,tot=0,a[12][12],vis[12][12],f[12][12][5019
];const
int dx[5]=;
const
int dy[5]=;
struct prepre[12][12][5019
];queue
< pair >q; //
spfa佇列
void spfa(int
sta);
if(!vis[wx][wy]) vis[wx][wy]=1
,q.push(make_pair(wx,wy));}}
}}void dfs(int x,int y,int
now)
intmain() //
記錄景點
for(int sta=0;sta<=(1
<1;sta++); //
情況1:(i,j)不同子樹的合併
} if(f[i][j][sta]<1e9) q.push(make_pair(i,j)),vis[i][j]=1
; } spfa(sta);
//用spfa更新情況2:相鄰節點到達情況+選擇此地的代價
}
int ansx,ansy,flag=0
;
for(int i=1;i<=n&&!flag;i++) for(int j=1;j<=m;j++)
if(!a[i][j]) //
隨意找乙個起點
printf("
%d\n
",f[ansx][ansy][(1
<1]); //
各處景點相連的代價一定相同
memset(vis,
0,sizeof(vis)); dfs(ansx,ansy,(1
<1
);
for(int i=1;i<=n;i++,puts("")) } }
#include#include——時間劃過風的軌跡,那個少年,還在等你#include
#include
#include
#include
#include
#include
#include
using
namespace
std;
typedef
long
long
ll;//
【p3264】管道連線
//[斯坦納樹]
//f[x][o] 即以x為根的子樹滿足二進位制狀態o時需要的最小代價。
//<1> f[x][o] = f[x][o1] + f[x][o-o1] ;
//<2> f[x][o] = f[p][o] + w[x][p] (點x與點p相鄰) ;
//g[o]表示,顏色聯通性為sta時的最小值,列舉子集轉移即可。
void reads(int &x)
while(s>='
0'&&s<='9')
x*=fx; //
正負號}
const
int n=1000019; struct nodee[n*2
];int n,m,p,tot=1
,head[n],vis[n],sum[n];
void
add(ll x,ll y,ll z)
struct pointa[n]; int f[2019][2019],g[2019
];queue
< int >q; //
spfa佇列
void spfa(int
sta)}}
}bool check(int
sta)
intmain() spfa(sta);
//用spfa更新情況2:相鄰節點到達情況+選擇此地的代價
}
for(int sta=0;sta<=(1
<1;sta++)
for(int i=1;i<=n;i++) g[sta]=min(g[sta],f[i][sta]);
for(int sta=0;sta<=(1
<1;sta++) if
(check(sta))
for(int s=sta;s;s=(s-1)&sta) if(check(s)) //
s可行,sta可行
g[sta]=min(g[sta],g[s]+g[sta-s]); //
那麼sta-s也一定可行
printf(
"%d\n
",g[(1
<1]); return0;
}
暖 墟 關於 樹 的專題
one.二叉樹 binary tree 由根結點 root 左子樹 left subtree 和右子樹 right subtree 組成,而左 右子樹分別是一棵二叉樹。注意在計算機中,樹一般是 倒置 的,即根在上,葉子在下。樹 tree 和二叉樹類似,區別在於每個結點不一定只有兩棵子樹。書的內容也是...
最小斯坦納樹 學習筆記
斯坦納樹問題是組合優化問題,與最小生成樹相似,是最短網路的一種。最小生成樹是在給定的點集和邊中尋求最短網路使所有點連通。而最小斯坦納樹允許在給定點外增加額外的點,使生成的最短網路開銷最小。可以這麼理解 乙個圖的生成樹是構造一棵樹把所有點給聯通,而斯坦納樹則是構造一棵樹把給定的幾個點聯通。如同生成樹有...
暖 墟 計算幾何 半平面交的學習與練習
其中每個半平面用一條有向直線表示,它的左側就是它所代表的半平面。半平面交通常是乙個凸多邊形,也有時候會得到乙個無界多邊形,甚至是線段 直線 點或 空 無論怎樣結果一定是 凸 的。一般用乙個很大的矩形 4個半平面的交 代替 整個平面 計算結果之後刪除這4個人工半平面。這樣,在計算的過程中,每加入乙個半...