分析:
第一次看到这道题,觉得就是一个MST
但是我明明找到的斯坦纳树的练习啊。。。
实际上不是很明白MST和斯坦纳树的区别,好像都是最小网络
不过斯坦纳树可以在给出点外另找点,并且不要求联通所有点
大概就是能处理有后效性的图上状压dp ???
浅谈斯坦纳树
时隔多年多月,要不是今天Mario_sz给我讲了一下,我可能永远都蒙在鼓里
这次终于通过这道题有一点点理解了
总的来说
首先我们设计状态:
f[i][j]
f
[
i
]
[
j
]
表示根为
i
i
,连通状态为的最小代价(状态只李记录关键点)
有两种转移方法:
- 枚举子树的形态: f[i][j]=min(f[i][j],f[i][k]+f[i][l]) f [ i ] [ j ] = m i n ( f [ i ] [ j ] , f [ i ] [ k ] + f [ i ] [ l ] ) ,其中 k k 和是对 j j 的一个划分
- 按照边进行松弛:,其中 i i 和之间有边相连
对于第一种转移,我们直接枚举子集
对于第二种转移,我们用spfa进行状态转移
枚举状态集S
{
枚举S的子集s
{
更新f[1~n][S]
}
将 f[x][S]<inf 的x入队
spfa(S)
}
这道题就会说构造包含 2d 2 d 个点的斯坦纳树
tip
斯坦纳树的状态只记录关键点
为什么狂WA不止?
因为初始化的时候要用
0x33
0
x
33
,不要乱用(反正不要用0x7f就能A了)
看到网上前辈的代码,在最后都会再转移一个
g
g
表示连通的状态为
s
s
的最小代价,枚举前个点的连通状况
S
S
为了满足题意,强制已经连通:
S Xor (S<<d)
S
X
o
r
(
S
<<
d
)
多了这个转移就是保证
i<−>n−i+1
i
<
−
>
n
−
i
+
1
一定连通的情况下
再转移
1−d
1
−
d
连通的最小代价(没有会WA哦)
for (int S=1;S<(1<<d);S++)
for (int i=1;i<=n;i++)
g[S]=min(g[S],f[S^(S<<d)][i]);
for (int S=1;S<(1<<d);S++)
for (int s=(S-1)&S;s;s=(s-1)&S)
g[S]=min(g[S],g[s]+g[S^s]);
为什么狂T不止?
一个很奇怪的现象:
我一开始设计的状态:
f[n][S]
f
[
n
]
[
S
]
,前一维表示点,后一维表示状态,
f[10000][260]
f
[
10000
]
[
260
]
怪异的T了
把两维的顺序换了一下:
f[S][n]
f
[
S
]
[
n
]
,前一维表示状态,后一维表示点,
f[260][10000]
f
[
260
]
[
10000
]
表面上没有任何区别,但是这样才能A掉
瑟瑟发抖,不知道什么原因,问了一下曲神
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
const int INF=0x33333333;
const int N=10005;
int f[260][N],g[260],st[N],tot=0,n,m,d;
struct node{
int y,v,nxt;
};
node way[N<<1];
bool in[N];
queue<int> q;
void add(int u,int w,int z) {
tot++;way[tot].y=w;way[tot].v=z;way[tot].nxt=st[u];st[u]=tot;
tot++;way[tot].y=u;way[tot].v=z;way[tot].nxt=st[w];st[w]=tot;
}
void spfa(int S) {
while (!q.empty()) {
int now=q.front(); q.pop();
in[now]=0;
for (int i=st[now];i;i=way[i].nxt) {
int y=way[i].y;
if (f[S][y]>f[S][now]+way[i].v) {
f[S][y]=f[S][now]+way[i].v;
if (!in[y]) {
in[y]=1;
q.push(y);
}
}
}
}
}
void solve() {
memset(f,0x33,sizeof(f));
int cnt=2*d;
for (int i=1;i<=d;i++) f[1<<i-1][i]=0,f[1<<d+i-1][n-i+1]=0;
for (int S=1;S<(1<<cnt);S++) {
for (int s=(S-1)&S;s;s=(s-1)&S) {
int t=S^s;
for (int i=1;i<=n;i++)
f[S][i]=min(f[S][i],f[s][i]+f[t][i]);
}
for (int i=1;i<=n;i++)
if (f[S][i]<INF&&!in[i])
q.push(i),in[i]=1;
spfa(S);
}
memset(g,0x33,sizeof(g));
for (int S=1;S<(1<<d);S++)
for (int i=1;i<=n;i++)
g[S]=min(g[S],f[S^(S<<d)][i]);
for (int S=1;S<(1<<d);S++)
for (int s=(S-1)&S;s;s=(s-1)&S)
g[S]=min(g[S],g[s]+g[S^s]);
printf("%d",g[(1<<d)-1]==INF? -1:g[(1<<d)-1]);
}
int main()
{
scanf("%d%d%d",&n,&m,&d);
for (int i=1;i<=m;i++) {
int u,w,z;
scanf("%d%d%d",&u,&w,&z);
add(u,w,z);
}
solve();
return 0;
}