1. 程式人生 > >BZOJ4006: [JLOI2015]管道連接(斯坦納樹,狀壓DP)

BZOJ4006: [JLOI2015]管道連接(斯坦納樹,狀壓DP)

add tps tput string submit space problems i++ 形式化

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;
}

BZOJ4006: [JLOI2015]管道連接(斯坦納樹,狀壓DP)