题目
n
n
n 个点
m
m
m 条边无向连通图,每条边有权值,指定
K
K
K 条边必须选,每条边可以经过多次,问从
1
1
1 出发遍历完必须边最后回到
1
1
1 的最小花费?
n
≤
13
,
0
≤
K
≤
78
,
2
≤
m
≤
200
n\le 13,0\le K\le 78,2\le m\le 200
n≤13,0≤K≤78,2≤m≤200
吃货JYY
思路
边走多次相当于是添加边/加度数
显然类似欧拉回路,经过点集
S
S
S 的度数为偶数就能完成
发现限制还是蛮多的:
- 走完必须边
- 走回路
- 费用最小
然后根据条件我们可以把点分成三类:未遍历的,奇点,偶点
由于指定
K
K
K 条边必须选,我们可以先将他们的贡献提取出来,转移时候只考虑他们对度数影响
考虑处理两点间最短路
d
i
s
dis
dis
记
f
S
f_S
fS 表示点的状态集合为
S
S
S 的最小代价,
f
2
=
0
f_2=0
f2=0
我们一个一个将未遍历的点加入集合
f
t
r
a
n
s
(
S
,
i
,
j
)
=
f
S
+
d
i
s
i
,
j
,
i
∈
S
,
j
∉
S
f_{trans(S,i,j)}=f_S+dis_{i,j},i\in S,j\not \in S
ftrans(S,i,j)=fS+disi,j,i∈S,j∈S
或者直接有必须边
f
t
r
a
n
s
(
S
,
i
,
j
)
=
f
S
,
n
d
i
,
j
&
i
∈
S
,
j
∉
S
f_{trans(S,i,j)}=f_S,nd_{i,j}\And i\in S,j\not \in S
ftrans(S,i,j)=fS,ndi,j&i∈S,j∈S
然后我们再处理
g
S
g_S
gS 表示将
S
S
S 中奇点两两连接最小代价
a
n
s
=
m
i
n
(
f
S
+
g
S
′
+
m
u
s
t
)
ans=min(f_S+g_{S'}+must)
ans=min(fS+gS′+must)
时间复杂度
O
(
3
n
n
2
)
O(3^nn^2)
O(3nn2)
代码
#include<set>
#include<map>
#include<cmath>
#include<deque>
#include<stack>
#include<ctime>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int read(){
bool f=0;int x=0;char c=getchar();
while(c<'0'||'9'<c){if(c=='-')f=1;c=getchar();}
while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return !f?x:-x;
}
#define fi first
#define se second
#define mp make_pair
const int MAXN=13;
const int MAXK=78;
const int MAXM=200;
const int MAXS=1594323;
const int INF=0x3f3f3f3f;
int pw3[15];
bool vis[MAXS+5];
int Q[MAXS+5],hd,tl;
int dis[MAXN+5][MAXN+5];
bool atc[MAXN+5][MAXN+5];
int f[MAXS+5],g[(1<<MAXN)+5];
int U[MAXM+5],V[MAXM+5],W[MAXM+5];
int n,K,val,Mu[MAXK+5],Mv[MAXK+5],Mw[MAXK+5];
void Watch(int s,int b){
int tmp=s;
for(int i=0;i<n;i++)
printf("%d ",s%b),s/=b;
puts("");
printf("%d\n",b==2?g[tmp]:f[tmp]);
return;
}
int main(){//f[s]:0/1/2:是否在集合内/奇偶性 时候最小代价
pw3[0]=1;
n=read(),K=read(),val=0;
for(int i=1;i<=n;i++)
pw3[i]=3*pw3[i-1];
memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=n;i++)
dis[i][i]=0;
int ms=0,key=0;
for(int i=1;i<=K;i++){
Mu[i]=read(),Mv[i]=read(),Mw[i]=read(),val+=Mw[i];
atc[Mu[i]][Mv[i]]=1;
atc[Mv[i]][Mu[i]]=1;
dis[Mu[i]][Mv[i]]=min(Mw[i],dis[Mu[i]][Mv[i]]),dis[Mv[i]][Mu[i]]=min(dis[Mv[i]][Mu[i]],Mw[i]);
ms|=(1<<(Mu[i]-1)),ms|=(1<<(Mv[i]-1));
key^=(1<<(Mu[i]-1)),key^=(1<<(Mv[i]-1));
}
int m=read();
for(int i=1;i<=m;i++)
U[i]=read(),V[i]=read(),W[i]=read(),dis[U[i]][V[i]]=min(dis[U[i]][V[i]],W[i]),dis[V[i]][U[i]]=min(dis[V[i]][U[i]],W[i]);
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
memset(g,0x3f,sizeof(g));
memset(f,0x3f,sizeof(f));
g[0]=0,f[2]=0;
for(int s=0;s<(1<<n);s++)
for(int x=0;x<n;x++)
if(!(s&(1<<x)))
for(int y=x+1;y<n;y++)
if(!(s&(1<<y)))
g[s|(1<<x)|(1<<y)]=min(g[s|(1<<x)|(1<<y)],g[s]+dis[x+1][y+1]);
hd=0,tl=1,Q[++hd]=2,vis[2]=1;
while(hd<=tl){
int s=Q[hd++];
//Watch(s,3);
for(int i=0;i<n;i++)
if(s/pw3[i]%3==0)
for(int j=0,b;j<n;j++)
if((b=s/pw3[j]%3)){
int id=s+pw3[i]+(b&1?1:-1)*pw3[j];
f[id]=min(f[id],f[s]+dis[i+1][j+1]);//!!!!
if(!vis[id]&&f[id]!=INF)
vis[id]=1,Q[++tl]=id;
id=s+2*pw3[i];
if(atc[i+1][j+1]){
f[id]=min(f[id],f[s]);
if(!vis[id]&&f[id]!=INF)
vis[id]=1,Q[++tl]=id;
}
}
}
int ans=INF;
for(int s=2;s<pw3[n];s++)
if(f[s]!=INF){
int ns=0;
for(int i=0;i<n;i++)
if(((ms>>i)&1)&&!(s/pw3[i]%3))
goto Break;
for(int i=0,t;i<n;i++){
t=s/pw3[i]%3;
if(t==1) ns|=(1<<i);
if((key>>i)&1)
ns^=(1<<i);
}
ans=min(ans,f[s]+g[ns]+val);
Break:;
}
printf("%d\n",ans);
return 0;
}