题目大意
给你一个n个点m条边的图,然后需要选择若干条边,使得 i i i 和 n − i + 1 n-i+1 n−i+1 联通且 1 < = i < = k 1<=i<=k 1<=i<=k ( k < = 4 ) (k<=4) (k<=4)
解题思路
介于我们是找若干个联通块使得两个点联通,那么我们可以分成两步来做。
一、对于每个联通块分别处理出它的最小代价
其实就是一个斯坦纳树。
由于是求最小代价,所以一个联通块一定是一棵树。
具体来说,设
f
i
,
S
f_{i,S}
fi,S 表示以第
i
i
i 个点为根,然后关键点间是否联通的状态为
S
S
S
然后列出方程:
设
T
∈
S
T\in S
T∈S ,则
f
i
,
S
=
m
i
n
{
f
i
,
T
+
f
i
,
S
⊕
T
}
f_{i,S}=min\{f_{i,T}+f_{i,S\oplus T}\}
fi,S=min{fi,T+fi,S⊕T}
但同时,我们还需要更新
S
S
S 相同时的情况
那么
f
v
,
S
=
m
i
n
{
f
u
,
S
+
w
v
,
u
}
f_{v,S}=min\{f_{u,S}+w_{v,u}\}
fv,S=min{fu,S+wv,u}
v
v
v 和
u
u
u 有连边
这个转移十分像最短路的,由此,我们也应该用spfa或dij做
二、合并联通块
我们设
g
[
S
]
g[S]
g[S] 表示使集合
S
S
S 联通的最小代价。
那么
g
[
S
]
=
m
i
n
{
m
i
n
i
=
1
n
{
f
i
,
S
}
,
g
[
T
]
+
g
[
S
⊕
T
]
}
g[S]=min\{min_{i=1}^n\{f_{i,S}\},g[T]+g[S\oplus T]\}
g[S]=min{mini=1n{fi,S},g[T]+g[S⊕T]} 且
S
,
T
,
S
⊕
T
S,T,S\oplus T
S,T,S⊕T 满足k对城市要不都联通要不都不连通。
最后输出即可。
Code
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e4+5;
struct node{
int to,next,w;
}e[2*N];
int cnt,n,k,m,INF;
int head[N],g[1<<10],f[N][1<<10];
bool vis[N];
queue<int>q;
void add(int u,int v,int w){
e[++cnt].to=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt;
return;
}
void spfa(int S){
while (!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for (int i=head[u];i;i=e[i].next){
int v=e[i].to;
if (f[v][S]>f[u][S]+e[i].w){
f[v][S]=f[u][S]+e[i].w;
if (!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
return;
}
bool pd(int S){
for (int i=1;i<=k;++i)
if (((S&(1<<(i-1)))>0)^((S&(1<<(2*k-i)))>0)) return 0;
return 1;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
int x,y,z;
for (int i=1;i<=m;++i){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
memset(f,127/3,sizeof(f));
INF=f[0][0];
for (int i=1;i<=k;++i)
f[i][1<<(i-1)]=f[n-i+1][1<<(2*k-i)]=0;
for (int S=1;S<(1<<(2*k));++S){
while (!q.empty()) q.pop();
for (int i=1;i<=n;++i){
for (int T=(S-1)&S;T;T=(T-1)&S)
f[i][S]=min(f[i][S],f[i][T]+f[i][S^T]);
if (f[i][S]!=INF) q.push(i),vis[i]=1;
else vis[i]=0;
}
spfa(S);
}
g[1]=INF;
for (int S=1;S<(1<<(2*k));++S,g[S]=INF)
for (int i=1;i<=n;++i)
g[S]=min(g[S],f[i][S]);
for (int S=1;S<(1<<(2*k));++S){
if (!pd(S)) continue;
for (int T=(S-1)&S;T;T=(T-1)&S){
if (!pd(T)||!pd(S^T)) continue;
g[S]=min(g[S],g[T]+g[S^T]);
}
}
if (g[(1<<(2*k))-1]!=INF) printf("%d",g[(1<<(2*k))-1]);
else puts("-1");
return 0;
}