最小斯坦纳树

1.1 最小斯坦纳树模型

是一个经典模型:给定一张图和图上的一些关键点,图上边有非负边权,要求选择一些边使得所有关键点联通并且边权和最小。

关键点数一般很小,假设点数、边数、关键点数分别为 n,m,k,那么算法的时间复杂度是 O(2kmlogn+3kn)

P6192 【模板】最小斯坦纳树

题意同上。

首先显然最终的生成子图一定是一棵树。我们对这棵隐树做类似树形 dp 的东西。先看看式子:

dpi,S 表示第 i 个节点为根的子树,包含 S 关键点集合的最小边权和。

有两个转移:

  • 对于 ijdpi,Sdpj,S+wi,j
  • 对于 TSdpi,Sdpi,T+dpi,ST

初始 dp,0=0,如果 ikeynodes 那么 dpi,i=0

其中 checkmin

我们分类讨论证明这个转移是正确的:

首先如果 ji 的父亲,那么会白白多算,是不可能真正转移到的。

然后如果 i 有若干个儿子,其可以从每一个儿子上转移而来,再通过类似树上背包的方式合并。

考虑转移顺序。显然有第二维度从低到高的拓扑结构。枚举第二维度,可以先做第二个转移,对于每一个 i,枚举子集耗费 3k。然后做第一个转移,这个转移式子是 dpjdpi+wi,j,发现很像 dijkstra 结构。于是可以用稍微变形的 dijkstra 做。

时间复杂度是 O(2kmlogn+3kn)

GYM 100722 D Ticket to Ride

【题意】
给定 nm 边的图,给定 4 个节点对,要求选出一些边使得这 4 对节点两两联通。

【分析】

可以考虑先 24 枚举哪些对节点是连在一起的,然后求最小斯坦纳树,再合并。

也可以去掉 24:发现 dp 的时候其实做了所有关键点集合的答案,再做一次 dp 合并即可。

map<string, int> mp; int n, m; int fa[33]; 
int get(int x) {if(fa[x] == x) return x; else return fa[x] = get(fa[x]);}
void merge(int x, int y) {x = get(x); y = get(y); fa[x] = y; }
vector<pii> g[33];  int key[4][2]; int dp[33][1<<8]; bool vis[33]; 
int ans[1<<4]; 
int getans(int state) {
    vector<int> pt; 
    f(i, 0, 3) if((state >> i) & 1) {
        pt.push_back(key[i][0]); pt.push_back(key[i][1]); 
    }
    sort(pt.begin(), pt.end()); 
    int cnt = unique(pt.begin(), pt.end()) - pt.begin() - 1; 
    f(i, 1, n) f(j, 0, (1 << (cnt + 1)) - 1) dp[i][j] = inf;
    f(i, 0, cnt) dp[pt[i]][(1<<i)] = 0;
    f(i, 1, n) dp[i][0] = 0; 
    f(i, 0, (1 << (cnt + 1)) - 1) {
        f(j, 1, n) {
            for(int t = i; t >= 0; t = (t - 1) & i) {
                cmin(dp[j][i], dp[j][t] + dp[j][i - t]); 
                if(t == 0) break;
            }
        }
        priority_queue<pii> q; 
        int mn = 0; 
        f(j, 1, n) vis[j] = 0; 
        f(j, 1, n)if(mn == 0 || dp[j][i] < dp[mn][i]) {mn = j;}
        q.push({-dp[mn][i], mn}); 
        while(!q.empty()) {
            pii now = q.top(); q.pop(); 
            if(vis[now.second]) continue;
            vis[now.second] = 1; cmin(dp[now.second][i], -now.first); 
            for(pii j : g[now.second]) {
                if(vis[j.first]) continue;
                q.push({-dp[now.second][i] - j.second, j.first}); 
            }
        }
    }
    int ret = inf; 
    f(i, 1, n) cmin(ret, dp[i][(1 << (cnt + 1)) - 1]); 
    return ret; 
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    //freopen();
    //freopen();
    //time_t start = clock();
    //think twice,code once.
    //think once,debug forever.
    while(cin >> n >> m && n != 0) {
        mp.clear(); f(i, 1, n) {string s; cin >> s; mp[s] = i; g[i].clear(); }
        f(i, 1, m) {
            string s, t; cin >> s >> t; int w; cin >> w; 
            g[mp[s]].push_back({mp[t], w}); 
            g[mp[t]].push_back({mp[s], w}); 
        }
        f(i, 0, 3) f(j, 0, 1) {
            string u; cin >> u; key[i][j] = mp[u]; 
        }
        f(i, 0, 15) ans[i] = getans(i);
        f(i, 0, 15) for(int j = i; ; j = (j - 1) & i) {
            cmin(ans[i], ans[j] + ans[i - j]); if(j == 0) break;
        }
        cout << ans[15] << endl; 
    }
    //time_t finish = clock();
    //cout << "time used:" << (finish-start) * 1.0 / CLOCKS_PER_SEC <<"s"<< endl;
    return 0;
}
posted @   OIer某罗  阅读(194)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
点击右上角即可分享
微信分享提示