很久以前就打算写一下的,无奈关于最小限度生成树的论文是在看不下去,觉得实现起来很复杂,昨天晚上在hyh的要求下坚持看完了论文并实现了出来。
题目大意是:
矮人虽小却喜欢乘坐巨大的轿车,轿车大到可以装下无论多少矮人。某天,N(N≤5000)个矮人打算到野外聚餐。为了集中到聚餐地点,矮人A要么开车到矮人B家中,留下自己的轿车在矮人B家,然后乘坐B的轿车同行;要么直接开车到聚餐地点,并将车停放在聚餐地。
虽然矮人的家很大,可以停放无数量轿车,但是聚餐地点却最多只能停放K辆轿车。现在给你一张加权无向图,它描述了N个矮人的家和聚餐地点,要你求出所有矮人开车的最短总路程。
[输入文件]
第一行是整数M(M<=49000),接下来M行描述了M条道路。每行形式如同:S1 S2 x,S1和S2均是大于0小于5000的整数,x小于等于1000。最后一行包含两个整数k,root。root表示聚餐的地点。
[输出文件]
仅一行,形式如同:Total miles driven: xxxXxx是整数,表示最短总路程。
[输入输出实例]
Picnic.in Picnic.out
5 1 2 1 2 3 1 3 4 1 4 5 1 5 6 1 1 1 Total miles driven: 5
my code:
#include
<
cstdio
>
#include
<
iostream
>
#include
<
string
>
#include
<
map
>
using
namespace
std;
const
int
maxn
=
50
;
int
G[maxn][maxn],G2[maxn][maxn];
//
G原图 G2去掉限度节点后的图
int
label[maxn],tot[maxn];
//
label对G2进行编号,统一连同分量的节点标号相同;tot[i]表示标号为i的节点个数
bool
u[maxn],G3[maxn][maxn],b[maxn],u2[maxn];
//
u:dfs编号时的标记;G3:最小生成树
int
k,root,m,minid,maxid,id,maxdel,me1,me2;
//
id表示当前强连同分量的标号值
map
<
string
,
int
>
namedic;
inline
int
min(
int
a,
int
b) {
return
a
<
b
?
a:b; }
inline
int
max(
int
a,
int
b) {
return
a
>
b
?
a:b; }
//
读入数据
void
input() {
int
x;
string
s1,s2;
namedic[
"
Park
"
]
=
1
;
minid
=
1
;root
=
1
;
maxid
=
1
;
memset(G,
0
,
sizeof
G);
scanf(
"
%d
"
,
&
m);
for
(
int
i
=
0
;i
<
m;i
++
) {
cin
>>
s1
>>
s2
>>
x;
if
(namedic[s1]
==
0
) namedic[s1]
=
(
++
maxid);
if
(namedic[s2]
==
0
) namedic[s2]
=
(
++
maxid);
G[namedic[s1]][namedic[s2]]
=
G[namedic[s2]][namedic[s1]]
=
x;
}
scanf(
"
%d
"
,
&
k);
}
//
dfs求强连同分量并标号
void
dfs(
int
v) {
u[v]
=
true
;
tot[id]
++
;
label[v]
=
id;
for
(
int
i
=
minid;i
<=
maxid;i
++
)
if
(G2[v][i]
>
0
&&!
u[i]) {
dfs(i);
}
}
//
计算从root->v出发的圈中除root<->v外的边的最大值maxdel
//
me1,me2记录最大边的端点
bool
circlemax(
int
v,
int
j) {
u2[v]
=
true
;
for
(
int
i
=
minid;i
<=
maxid;i
++
) {
if
(
!
G3[v][i])
continue
;
if
(u2[i])
continue
;
if
(i
==
root) {
maxdel
=
j;
return
true
;
}
if
(j
<
G[v][i]) {
me1
=
v;me2
=
i;
}
if
(circlemax(i,max(j,G[v][i])))
return
true
;
}
return
false
;
}
//
对k限度生成树进行一次换边操作
int
addmstedge() {
int
bestdel
=-
1
,bestret
=
999999
,me1b,me2b;
int
addv;
for
(
int
i
=
minid;i
<=
maxid;i
++
) {
if
(G[root][i]
==
0
)
continue
;
if
(G3[root][i]
==
true
)
continue
;
memset(u2,
false
,
sizeof
u2);
circlemax(i,
0
);
//
printf("%d\n",maxdel);
if
(G[root][i]
-
maxdel
<
bestret) {
bestret
=
G[root][i]
-
maxdel;
addv
=
i;
me1b
=
me1,me2b
=
me2;
}
}
//
printf("%d\n",bestret);
G3[root][addv]
=
G3[addv][root]
=
true
;
G3[me1b][me2b]
=
G3[me2b][me1b]
=
false
;
//
printf("%d %d %d\n",addv,me1b,me2b);
if
(bestret
==
999999
)
return
0
;
else
return
bestret;
}
//
求连同分量并标号
void
make_cc() {
for
(
int
i
=
minid;i
<=
maxid;i
++
) {
for
(
int
j
=
minid;j
<=
maxid;j
++
) {
G2[i][j]
=
G[i][j];
if
(i
==
root
||
j
==
root) G2[i][j]
=
0
;
}
}
memset(u,
false
,
sizeof
u);
memset(tot,
0
,
sizeof
tot);
id
=
0
;u[root]
=
true
;
for
(
int
i
=
minid;i
<=
maxid;i
++
) {
if
(i
!=
root)
if
(
!
u[i]) {
id
++
;
dfs(i);
}
}
}
//
计算标号为id的连同分量的最小生成树
int
calc_mst(
int
id) {
int
sp,ep,sum
=
0
;
for
(
int
i
=
minid;i
<=
maxid;i
++
) {
if
(label[i]
==
id) {
b[i]
=
true
;
break
;
}
}
for
(
int
i
=
1
;i
<
tot[id];i
++
) {
int
min
=
999999
;
for
(
int
j
=
minid;j
<=
maxid;j
++
) {
if
(label[j]
!=
id)
continue
;
if
(
!
b[j])
continue
;
for
(
int
kk
=
minid;kk
<=
maxid;kk
++
) {
if
(label[kk]
!=
id)
continue
;
if
(b[kk])
continue
;
if
(G2[j][kk]
==
0
)
continue
;
if
(G2[j][kk]
<
min) {
min
=
G2[j][kk];
sp
=
j;
ep
=
kk;
}
}
}
sum
+=
min;
b[ep]
=
true
;
G3[sp][ep]
=
G3[ep][sp]
=
true
;
}
return
sum;
}
void
solve() {
int
sum
=
0
;
make_cc();
memset(b,
false
,
sizeof
b);
memset(G3,
false
,
sizeof
G3);
for
(
int
i
=
1
;i
<=
id;i
++
)
sum
+=
calc_mst(i);
for
(
int
i
=
1
;i
<=
id;i
++
) {
int
min
=
999999
;
int
nowid;
for
(
int
j
=
minid;j
<=
maxid;j
++
) {
if
(label[j]
!=
i)
continue
;
if
(G[root][j]
==
0
)
continue
;
if
(G[root][j]
<
min) {
min
=
G[root][j];
nowid
=
j;
}
}
sum
+=
min;
G3[root][nowid]
=
G3[nowid][root]
=
true
;
}
//
for(int i=minid;i<=maxid;i++)
//
for(int j=i;j<=maxid;j++)
//
if(G3[i][j]) printf("%d %d\n",i,j);
//
printf("%d\n",sum);
int
bestans
=
sum,ret
=
bestans;
for
(
int
nowx
=
id;nowx
<
k;nowx
++
) {
bestans
=
bestans
+
addmstedge();
ret
=
min(bestans,ret);
}
printf(
"
Total miles driven: %d\n
"
,ret);
}
int
main() {
input();
solve();
return
0
;
}