前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >BZOJ4006: [JLOI2015]管道连接(斯坦纳树,状压DP)

BZOJ4006: [JLOI2015]管道连接(斯坦纳树,状压DP)

作者头像
attack
发布2018-05-30 14:09:27
4883
发布2018-05-30 14:09:27
举报

Time Limit: 30 Sec  Memory Limit: 128 MB

Submit: 1171  Solved: 639

[Submit][Status][Discuss]

Description

小铭铭最近进入了某情报部门,该部门正在被如何建立安全的通道连接困扰。

该部门有 n 个情报站,用 1 到 n 的整数编号。给出 m 对情报站 ui;vi 和费用 wi,表示情

报站 ui 和 vi 之间可以花费 wi 单位资源建立通道。

如果一个情报站经过若干个建立好的通道可以到达另外一个情报站,那么这两个情报站就

建立了通道连接。形式化地,若 ui 和 vi 建立了通道,那么它们建立了通道连接;若 ui 和 vi 均

与 ti 建立了通道连接,那么 ui 和 vi 也建立了通道连接。

现在在所有的情报站中,有 p 个重要情报站,其中每个情报站有一个特定的频道。小铭铭

面临的问题是,需要花费最少的资源,使得任意相同频道的情报站之间都建立通道连接。

Input

第一行包含三个整数 n;m;p,表示情报站的数量,可以建立的通道数量和重要情报站的数

量。接下来 m 行,每行包含三个整数 ui;vi;wi,表示可以建立的通道。最后有 p 行,每行包含

两个整数 ci;di,表示重要情报站的频道和情报站的编号。

Output

输出一行一个整数,表示任意相同频道的情报站之间都建立通道连接所花费的最少资源总量。

Sample Input

5 8 4 1 2 3 1 3 2 1 5 1 2 4 2 2 5 1 3 4 3 3 5 1 4 5 1 1 1 1 2 2 3 2 4

Sample Output

4

HINT

选择 (1; 5); (3; 5); (2; 5); (4; 5) 这 4 对情报站连接。

对于 100% 的数据,0 <ci <= p <= 10; 0 <ui;vi;di <= n <= 1000; 0 <= m <= 3000; 0 <= wi <=

20000。

Source

斯坦纳森林

设$f[i][sta]$表示$i$号节点,与关键节点的联通性为$sta$时的最小值

假设我们已经求出了$f$

那么我们令$g[sta]$表示,颜色联通性为$sta$时的最小值

$g$比较好求,枚举子集转移就可以

$f$的话,如果学过斯坦纳树也比较好求

按照套路,两种转移方法,一种是枚举子集,一种是SPFA

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int MAXN = 1e6 + 10;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
    return x * f;
}
int N, M, P;
struct node {
    int u, v, w, nxt;
}E[MAXN];
int head[MAXN], num = 1;
inline void AddEdge(int x, int y,int z) {
    E[num].u = x; E[num].v = y; E[num].w = z;
    E[num].nxt = head[x]; head[x] = num++;
}
struct Point {
    int color, ID;
}a[MAXN];
int f[1051][1051], g[1051];
int vis[MAXN], sum[MAXN];
queue<int>q;
void SPFA(int now) {
    while(q.size() != 0) {
        int p = q.front(); q.pop(); vis[p] = 0;
        for(int i = head[p]; i != -1; i = E[i].nxt) {
            int v = E[i].v;
            if(f[v][now] > f[p][now] + E[i].w) {
                f[v][now] = f[p][now] + E[i].w;
                if(!vis[v]) vis[v] = 1, q.push(v);
            }
        }
    }
}
int tmp[11];
bool check(int sta) {
    memset(tmp, 0, sizeof(tmp));
    for(int i = 1; i <= 10; i++)
        if(sta & (1 << i - 1)) tmp[a[i].color]++;
    for(int i = 1; i <= 10; i++)
        if(tmp[i] && tmp[i] != sum[i]) return 0;
    return 1;
}
int main() {
    #ifdef WIN32
    freopen("a.in", "r", stdin);
    #endif
    memset(head, -1, sizeof(head));
    N = read(), M = read(), P = read();
    for(int i = 1; i <= M; i++) {
        int x = read(), y = read(), z = read();
        AddEdge(x, y, z);AddEdge(y, x, z);
    }
    memset(f, 0x3f, sizeof(f));
    memset(g, 0x3f, sizeof(g));
    for(int i = 1; i <= P; i++) 
        a[i].color = read(), a[i].ID = read(), sum[a[i].color]++,f[a[i].ID][1 << (i - 1)] = 0;        
    int limit = (1 << P) - 1;    
    for(int sta = 0; sta <= limit; sta++) {
        for(int i = 1; i <= N; i++) {
            for(int S = sta; S; S = (S - 1) & sta) 
                f[i][sta] = min(f[i][sta], f[i][S] + f[i][sta - S]);
            q.push(i),vis[i] = 1;
        }
        SPFA(sta);    
    }
    for(int sta = 0; sta <= limit; sta++) 
        for(int i = 1; i <= N; i++)
            g[sta] = min(g[sta], f[i][sta]);
    for(int sta = 0; sta <= limit; sta++)
        if(check(sta))
            for(int S = sta; S; S = (S - 1) & sta)
                if(check(S))
                    g[sta] = min(g[sta], g[S] + g[sta - S]);
    printf("%d", g[(1 << P) - 1]);
    return 0;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-05-01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Description
  • Input
  • Output
  • Sample Input
  • Sample Output
  • HINT
  • Source
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档