BZOJ 3590 [双连通分量][状压DP]

来源:互联网 发布:qq国际版for ubuntu 编辑:程序博客网 时间:2024/05/18 02:04

Description

求一个包含所有节点的边双联通分量,并是边权和最小。n12

Solution

注意到任意的一个边双连通分量都是由一条链和一个子双连通分量构成的,而且题中节点数n12。考虑状压DP。

fS表示点集为S的双联通分量的权值的最小值。

hS,u,0/1表示边(u,v)(vS)的权值的最小值以及次小值。

gS,u,v表示点集S的以u,v为端点的链的权值的最小值。

则可以通过h,g数组更新f。时间复杂度O(2nn2)

#include <cstring>#include <iostream>using namespace std;inline char get(void) {  static char buf[100000], *S = buf, *T = buf;  if (S == T) {    T = (S = buf) + fread(buf, 1, 100000, stdin);    if (S == T) return EOF;  }  return *S++;}inline void read(int &x) {  static char c; x = 0;  for (c = get(); c < '0' || c > '9'; c = get());  for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';}const int INF = 0x1f1f1f1f;const int M = 15;const int N = (1 << 12) + 10;int test, n, m, x, y, z, Sn, mn, se, Gcnt;int id[M], head[M];int f[N], h[N][M][2], g[N][M][M];struct edge {  int to, next, key;  edge(int t = 0, int n = 0, int k = 0):to(t), next(n), key(k) {}};edge G[M * M * 2];inline int Min(int a, int b) {  return a < b ? a : b;}inline void AddEdge(int from, int to, int key) {  G[++Gcnt] = edge(to, head[from], key); head[from] = Gcnt;  G[++Gcnt] = edge(from, head[to], key); head[to] = Gcnt;}int main(void) {  freopen("1.in", "r", stdin);  freopen("1.out", "w", stdout);  read(test);  for (int i = 0; i < 12; i++) id[i + 1] = 1 << i;  while (test--) {    memset(f, 0x1f, sizeof f);    memset(g, 0x1f, sizeof g);    Gcnt = 0; memset(head, 0, sizeof head);    read(n); read(m); Sn = 1 << n;    for (int i = 1; i <= m; i++) {      read(x); read(y); read(z);      AddEdge(x, y, z);    }    for (int i = 1; i <= n; i++)      f[id[i]] = g[id[i]][i][i] = 0;    for (int S = 0; S < Sn; S++) {      for (int i = 1; i <= n; i++) {        mn = INF; se = INF;        for (int j = head[i]; j; j = G[j].next) {          if (id[G[j].to] & S) {            if (G[j].key <= mn) {              se = mn; mn = G[j].key;            } else if (G[j].key < se){              se = G[j].key;            }          }        }        h[S][i][0] = mn; h[S][i][1] = se;      }     }    for (int S = 1; S < Sn; S++)      for (int i = 1; i <= n; i++)        if (id[i] & S)          for (int j = 1; j <= n; j++)            if (id[j] & S) {              for (int p = head[i]; p; p = G[p].next)                if(!(id[G[p].to] & S))                  g[S | id[G[p].to]][G[p].to][j] = Min(g[S | id[G[p].to]][G[p].to][j], g[S][i][j] + G[p].key);              for (int p = head[j]; p; p = G[p].next)                if(!(id[G[p].to] & S))                  g[S | id[G[p].to]][i][G[p].to] = Min(g[S | id[G[p].to]][i][G[p].to], g[S][i][j] + G[p].key);            }    for (int S = 1; S < Sn; S++) {      int R = (Sn - 1) ^ S;      for (int T = R; T; T = (T - 1) & R) {        for (int i = 1; i <= n; i++)          if (id[i] & T)            for (int j = 1; j <= n; j++)              if (id[j] & T)                if (i == j)                  f[S | T] = Min(f[S | T], f[S] + h[S][i][0] + h[S][j][1] + g[T][i][j]);                else                  f[S | T] = Min(f[S | T], f[S] + h[S][i][0] + h[S][j][0] + g[T][i][j]);      }    }    if (f[Sn - 1] >= INF) puts("impossible");    else printf("%d\n", f[Sn - 1]);  }  return 0;}
原创粉丝点击