LuoguP3959 宝藏 题解
Posted riverhamster
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LuoguP3959 宝藏 题解相关的知识,希望对你有一定的参考价值。
思路主要是抄_rqy的,这神仙位运算tql,整理一下思路。
题目大意
给定\(n\)个点,\(m\)条有权边,从一个点\(s\)开始挖(任选),形成一个生成树(即已经挖通的两个点间不能连边),挖一条边的代价为边的长度乘\(s\)到边的终点的距离,求最小代价。
\(n \leq 12\)
思路
状压dp。
和一般的状压dp不一样,需要进行两层状压。
设\(f[S][d][i]\)为挖开点集\(S\),现在距离起点的距离为\(d\),所在的点为\(i\)的最小代价。
则\[f[S][d][i] = \min_S_1\subset S^k\in S1 w(i, k)(d+1) + f[S1-\k\][d+1][k] + f[S-S1][d][i]\]
其中\(i \notin S, d+|S| \leq n\),因为当前至少已经挖开了\(d\)个点,最多只能挖开\(n-d\)个点。
初始化:\(f[0][d][i] = 0\)
注意\(S\)从小到大,\(d\)从大到小。
最终结果就是\[\min_i=1^nS[U-\i\][0][i]\]
复杂度
对于整个图,分成子集大小是\(1\)到\(n\)的考虑。
\[T(n) = \sum_i=1^n C_n^i 2^i = (2+1)^n = 3^n\]
(二项式定理,大小为\(i\)的集合有\(C_n^i\)个,它的子集有\(2^i\)个)
Code
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
#define File(IO_Filename) freopen(IO_Filename".in", "r", stdin), freopen(IO_Filename".out", "w", stdout)
typedef long long ll;
template<typename T> inline void in(T &x)
char ch; x = 0;
while(isspace(ch = getchar()));
do x = x * 10 + ch - '0'; while(isdigit(ch = getchar()));
const int N = 13, INF = 0x3f3f3f3f;
int w[N][N], pc[1<<N], low[1<<N]; //pc表示集合的元素个数,low表示集合内最小的元素(用来取子集)
int f[1<<N][N][N];
void wrbin(int x)
if(x == 0) return ;
wrbin(x >> 1);
putchar('0' + (x & 1));
int main()
// File("3959");
int n, m, x, y, wt, tot;
in(n); in(m);
memset(w, 0x3f, sizeof(w)); memset(f, 0x3f, sizeof(f));
for(int i=1; i<=m; i++)
in(x); in(y); in(wt); --x; --y;
w[x][y] = w[y][x] = min(w[x][y], wt);
tot = 1 << n;
for(int i=0; i<tot; i++) pc[i] = pc[i & (i-1)] + 1;
for(int i=0; (1 << i) < tot; i++) low[1 << i] = i;
for(int i=0; i<tot; i++) low[i] = low[i & (-i)];
for(int d=n-2; d>=0; d--)
for(int i=0; i<n; ++i)
f[0][d][i] = 0;
for(int d=n-2; d>=0; --d)
for(int i=0; i<n; ++i)
for(int S=1; S<tot; ++S) if(pc[S] <= n - d && (S & (1 << i)) == 0) //优化
for(int S1=S; S1; S1=(S1-1)&S) //取S的子集S1
for(int t=S1, k=low[t]; t; t&=(t-1), k=low[t]) //取S1的所有元素
if(w[i][k] != INF && f[S1-(1<<k)][d+1][k] != INF && f[S-S1][d][i] != INF)
f[S][d][i] = min(f[S][d][i],
w[i][k] * (d+1) + f[S1-(1 << k)][d+1][k] + f[S-S1][d][i]);
int ans = INF;
for(int i=0; i<n; i++)
ans = min(ans, f[tot-1-(1<<i)][0][i]);
printf("%d\n", ans);
return 0;
以上是关于LuoguP3959 宝藏 题解的主要内容,如果未能解决你的问题,请参考以下文章