修路 BZOJ 4774

修路

【问题描述】

村子间的小路年久失修,为了保障村子之间的往来,法珞决定带领大家修路。对于边带权的无向图 G = (V, E),请选择一些边,使得1 <= i <= d, i号节点和 n - i + 1 号节点可以通过选中的边连通,最小化选中的所有边的权值和。

【输入格式】

第一行两个整数 n, m,表示图的点数和边数。接下来的 m行,每行三个整数 ui, vi, wi,表示有一条 ui 与 vi 之间,权值为 wi 的无向边。

【输出格式】

一行一个整数,表示答案,如果无解输出-1

【样例输入】

10 20 1
6 5 1
6 9 4
9 4 2
9 4 10
6 1 2
2 3 6
7 6 10
5 7 1
9 7 2
5 9 10
1 6 8
4 7 4
5 7 1
2 6 9
10 10 6
8 7 2
10 9 10
1 2 4
10 1 8
9 9 7

【样例输出】

8

【数据范围】

1 <= d <= 4
2d <= n <= 10^4
0 <= m <= 10^4
1 <= ui, vi <= n
1 <= wi <= 1000

题解:

考虑到d灰常小,就是斯坦纳树啦

用斯坦纳树求出以u为根,连通情况为opt的最小价值,记为f[u][opt]

由于题目只要 i 与 n - i + 1 连通,那么取出所有对称的opt,取最小值,记为ans[opt]

再暴力Dp合并

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<cstdio>
  6 #include<cmath>
  7 using namespace std;
  8 inline void Scan(int &x)
  9 {
 10     char c;
 11     while((c = getchar()) < '0' || c > '9');
 12     x = c - '0';
 13     while((c = getchar()) >= '0' && c <= '9') x = x * 10 + c - '0';
 14 }
 15 const int maxn = 100233;
 16 const int inf = 707406378;
 17 int n, m, d, t, w;
 18 int num, all;
 19 bool vis[maxn];
 20 int ans[301], que[maxn];
 21 int f[maxn][301];
 22 int tot, nex[maxn], fir[maxn], ver[maxn], pri[maxn];
 23 inline void Ins(int x, int y, int z)
 24 {
 25     nex[++tot] = fir[x];
 26     fir[x] = tot;
 27     ver[tot] = y;
 28     pri[tot] = z;
 29 }
 30 inline void Reset()
 31 {
 32     memset(f, 127 / 3, sizeof(f));
 33     for(int i = 1; i <= d; ++i) f[i][1 << num++] = 0;
 34     for(int i = n - d + 1; i <= n; ++i) f[i][1 << num++] = 0;
 35     all = 1 << num;
 36 }
 37 inline void Spfa(int opt)
 38 {
 39     t = 0;
 40     while(t < w)
 41     {
 42         int u = que[++t];
 43         for(int i = fir[u]; i; i = nex[i])
 44         {
 45             int v = ver[i];
 46             if(f[u][opt] + pri[i] < f[v][opt])
 47             {
 48                 f[v][opt] = f[u][opt] + pri[i];
 49                 if(!vis[v])
 50                 {
 51                     vis[v] = true;
 52                     que[++w] = v;
 53                 }
 54             }
 55         }
 56         vis[u] = false;
 57     }
 58 }
 59 int main()
 60 {
 61     Scan(n), Scan(m), Scan(d);
 62     int x, y, z;
 63     for(int i = 1; i <= m; ++i)
 64     {
 65         Scan(x), Scan(y), Scan(z);
 66         Ins(x, y, z), Ins(y, x, z);
 67     }
 68     Reset();
 69     for(int opt = 0; opt < all; ++opt)
 70     {
 71         w = 0;
 72         for(int i = 1; i <= n; ++i)
 73         {
 74             for(int sub = opt; sub; sub = (sub - 1) & opt)
 75                 f[i][opt] = min(f[i][opt], f[i][sub] + f[i][opt ^ sub]);
 76             if(f[i][opt] != inf) que[++w] = i, vis[i] = true;
 77         }
 78         Spfa(opt);
 79         ans[opt] = inf;
 80     }
 81     bool flag;
 82     for(int opt = 0; opt < all; ++opt)
 83     {
 84         flag = false;
 85         for(int i = 0; i <= d; ++i)
 86         {
 87             bool a = (opt & (1 << i)) != 0;
 88             bool b = (opt & (1 << (d << 1) - i - 1)) != 0;
 89             if(a != b)
 90             {
 91                 flag = true;
 92                 break;
 93             }
 94         }
 95         if(flag) continue;
 96         for(int i = 1; i <= n; ++i) ans[opt] = min(ans[opt], f[i][opt]);
 97     }
 98     for(int opt = 0; opt < all; ++opt)
 99         for(int sub = opt; sub; sub = (sub - 1) & opt)
100             ans[opt] = min(ans[opt], ans[sub] + ans[opt ^ sub]);
101     if(ans[all - 1] != inf) printf("%d", ans[all - 1]);
102     else printf("-1");
103 }
posted @ 2017-03-18 20:44  草根柴鸡  阅读(193)  评论(0编辑  收藏  举报