1. 程式人生 > >[JLOI2015]管道連接

[JLOI2015]管道連接

一行 建立 ios std oid 頻道 描述 安全 之間

題目描述

小銘銘最近進入了某情報部門,該部門正在被如何建立安全的通道連接困擾。該部門有 n 個情報站,用 1 到 n 的整數編號。給出 m 對情報站 ui;vi 和費用 wi,表示情報站 ui 和 vi 之間可以花費 wi 單位資源建立通道。

如果一個情報站經過若幹個建立好的通道可以到達另外一個情報站,那麽這兩個情報站就建立了通道連接。形式化地,若 ui 和 vi 建立了通道,那麽它們建立了通道連接;若 ui 和 vi 均與 ti 建立了通道連接,那麽 ui 和 vi 也建立了通道連接。

現在在所有的情報站中,有 p 個重要情報站,其中每個情報站有一個特定的頻道。小銘銘面臨的問題是,需要花費最少的資源,使得任意相同頻道的情報站之間都建立通道連接。

輸入輸出格式

輸入格式:

第一行包含三個整數 n;m;p,表示情報站的數量,可以建立的通道數量和重要情報站的數量。接下來 m 行,每行包含三個整數 ui;vi;wi,表示可以建立的通道。最後有 p 行,每行包含兩個整數 ci;di,表示重要情報站的頻道和情報站的編號。

輸出格式:

輸出一行一個整數,表示任意相同頻道的情報站之間都建立通道連接所花費的最少資源總量。

輸入輸出樣例

輸入樣例#1:
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
輸出樣例#1:
4

說明

選擇 (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。

首先做一遍裸的斯坦納樹

f[i][sta]=min{f[i][sub]+f[i][sta-sub]}

因為題目可能出現斯坦納森林,所以還要進行一次子集dp

將每一種頻道作為二進制為儲存,還要計算出ki[i]表示i頻道需要覆蓋的點(二進制數)

枚舉1<=sta<(1<<p),k=sta頻道狀態需要覆蓋的點

dp[sta]=min(f[j][k]) j=1~n

dp[sta]=min(dp[sta],dp[sub]+dp[sta-sub])

答案是dp[(1<<p)]

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 using namespace std;
 7 struct Node
 8 {
 9   int next,to,dis;
10 }edge[60001];
11 int num,head[10001],f[1001][2001],dp[2001],ki[21],n,m,p;
12 bool vis[1001];
13 void add(int u,int v,int dis)
14 {
15   num++;
16   edge[num].next=head[u];
17   head[u]=num;
18   edge[num].to=v;
19   edge[num].dis=dis;
20 }
21 void bfs(int s)
22 {int i;
23   memset(vis,0,sizeof(vis));
24   queue<int> Q;
25   for (i=1;i<=n;i++)
26     vis[i]=1,Q.push(i);
27   while (Q.empty()==0)
28     {
29       int u=Q.front();
30       //cout<<u<<endl;
31       Q.pop();
32       vis[u]=0;
33       for (i=head[u];i;i=edge[i].next)
34     {
35       int v=edge[i].to;
36       if (f[v][s]>f[u][s]+edge[i].dis)
37         {
38           f[v][s]=f[u][s]+edge[i].dis;
39           if (vis[v]==0)
40         {
41           vis[v]=1;
42           Q.push(v);
43         }
44         }
45     }
46     }
47 }
48 int main()
49 {int i,u,v,w,x,y,j,k;
50   cin>>n>>m>>p;
51   for (i=1;i<=m;i++)
52     {
53       scanf("%d%d%d",&u,&v,&w);
54       add(u,v,w);add(v,u,w);
55     }
56   memset(f,127/3,sizeof(f));
57   for (i=1;i<=p;i++)
58     {
59       scanf("%d%d",&x,&y);
60       ki[x]|=(1<<i-1);
61       f[y][1<<i-1]=0;
62     }
63   for (i=1;i<(1<<p);i++)
64     {
65       for (j=1;j<=n;j++)
66     {
67       for (k=i;k;k=(k-1)&i)
68         if (f[j][i]>f[j][k]+f[j][i-k])
69           f[j][i]=f[j][k]+f[j][i-k];
70     }
71       bfs(i);
72     }
73   memset(dp,127/2,sizeof(dp));
74   for (i=1;i<(1<<p);i++)
75     {int k=0;
76       for (j=1;j<=p;j++)
77     if (i&(1<<j-1)) k|=ki[j];
78       for (j=1;j<=n;j++)
79     if (dp[i]>f[j][k])
80     dp[i]=f[j][k];
81       for (j=i;j;j=(j-1)&i)
82     if (dp[j]+dp[i-j]<dp[i])
83     dp[i]=dp[j]+dp[i-j];
84     }
85   cout<<dp[(1<<p)-1];
86 }

[JLOI2015]管道連接