随笔- 32  文章- 0  评论- 5  阅读- 31849 

话说挺早就写过斯坦纳树了,不过当时没怎么总结,也不是很理解……现在来个小结吧~

斯坦纳树就是包含给定点的最小生成树(个人理解权值应当为正)。

一般来讲,给定点的数目应该很小吧。。。于是我们可以用状压DP来解决。

需要2个方程:

f[st][i]表示连通性至少为st,且经过i点的最小距离

方程1.f[st][i] = Min{f[s][i] + f[st - s][i]}(s为st的子集)

方程2.f[st][i] = Min{f[st][j] + w(i,j)}(i,j之间有边相连)

 

这里大家可能会有疑问,为什么是两个方程?

因为单凭第一个方程转移是不够准确的,会出现重点的问题。不过也一定包含合法的转移,所以我们要通过第二个方程来转移。如果我没有理解错,这也就是为什么这种方法不能应用于有负权的图上。

第一个直接枚举子集,完了以后用SPFA转移下一个(当然也可以用floyed)。

void SPFA(int sta)
{
    while (!q.empty()) {
        int i,j;
        unpack(q.front(),i,j);
        inq[q.front()] = 0;q.pop();
        for (int k = 0; k < 4; ++k) {
            int x = d[k][0] + i,y = d[k][1] + j,tmp;
            if (x == -1 || y == -1 || x == n || y == m) continue;
            if (update(x,y,sta,i,j,sta,f[i][j][sta] + a[x][y]) && !inq[tmp = pack(x,y)])
                q.push(tmp),inq[tmp] = 1;
        }
    }
}

要记住f[st][i]表示连通性至少为st,是至少。

for (int sta = 1,tmp; sta < Max_s; ++sta) {
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < m; ++j) {
            for (int s = sta&(sta - 1); s; s = (s - 1)&sta)
                update(i,j,sta,i,j,s,f[i][j][s] + f[i][j][sta - s] - a[i][j]);
            if (f[i][j][sta] != INF) q.push(tmp = pack(i,j)),inq[tmp] = 1;
        }
    SPFA(sta);
}

 

这样转移就可以了。

  

 posted on   Lazycal  阅读(2451)  评论(0)    收藏  举报
点击右上角即可分享
微信分享提示