花了好长时间去理解题意,看来英语阅读能力还有待提高。
题意大概是这样的:用一个7位的string代表一个编号,两个编号之间的distance代表这两个编号之间不同字母的个数。一个编号只能由另一个编号“衍生”出来,代价是这两个编号之间相应的distance,现在要找出一个“衍生”方案,使得总代价最小,也就是distance之和最小。
例如有如下4个编号:
aaaaaaa
baaaaaa
abaaaaa
aabaaaa
显然的,第二,第三和第四编号分别从第一编号衍生出来的代价最小,因为第二,第三和第四编号分别与第一编号只有一个字母是不同的,相应的distance都是1,加起来是3。也就是最小代价为3。
问题可以转化为最小代价生成树的问题。因为每两个结点之间都有路径,所以是完全图。首先考虑用Kruskal算法解题,配合Disjoint Set Forest可以达到O(E lgV)。可是最后却无情的TLE了。分析TLE原因,原来K算法需要对所有边进行一次排序,当边数目比较多时,这就是非常耗时的一个工作。尤其本题是完全图,其代价可想而知。
以下是TLE版本:
Code
1 //关键字:Disjoint-set, minimal spanning tree: Kruskal, prime
2 #include <stdio.h>
3 #include <stdlib.h>
4 #define MAX 2000
5 typedef struct{
6 int rank;
7 int parent;
8 char string[7];
9 }VERTEX;
10 VERTEX vertex[MAX];
11 int distance[MAX][MAX];
12 int sortDis[MAX*(MAX-1)/2];
13 //--------------------DisJoint Set Operations----------------
14 void MakeSet(VERTEX *v, int x)
15 {
16 v[x].rank=0;
17 v[x].parent=x;
18 }
19 int FindSet(VERTEX *v, int x)
20 {
21 if (x != v[x].parent)
22 v[x].parent=FindSet(v, v[x].parent);
23 return v[x].parent;
24 }
25 void Link(VERTEX *v, int x, int y)
26 {
27 if (v[x].rank > v[y].rank)
28 v[y].parent=x;
29 else{
30 v[x].parent=y;
31 if (v[x].rank==v[y].rank)
32 v[y].rank++;
33 }
34 }
35 void Union(VERTEX *v, int x, int y)
36 {
37 Link(v, FindSet(v, x), FindSet(v, y));
38 }
39
40 //------------------------------------------------------------
41 int cmp(const void *a, const void *b)
42 {
43 return (distance[(*(int *)a)/MAX][(*(int *)a)%MAX] >= distance[(*(int *)b)/MAX][(*(int *)b)%MAX]) ? 1: -1;
44 }
45 main()
46 {
47 int n, i, j, k, dif, eageCount=-1, Q, va, vb;
48 VERTEX *v;
49 v=vertex;
50 //freopen("input.txt", "r", stdin);
51 while (scanf("%d", &n) && n!=0){
52 //输入字符串并构造不相交集的最小集
53 for (i=0; i<n; i++){
54 scanf("%s\n", (v+i)->string);
55 MakeSet(v, i);
56 }
57 //计算字符串中不同字符的个数,保存到distance[][],
58 //distance[][]二维数组只用到上右半个三角区域
59 for (i=0; i<n-1; i++){
60 for (j=i+1; j<n; j++){
61 eageCount++;
62 dif=0;
63 for (k=0; k<7; k++){
64 if ((v+i)->string[k] != (v+j)->string[k]){
65 dif++;
66 }
67 }
68 distance[i][j]=dif;
69 sortDis[eageCount]=i*MAX+j;
70 }
71 }
72 //对distance[][]进行从小到大的排序,排序结构保存到sortDis[]当中
73 qsort(sortDis, eageCount+1, sizeof(int), cmp);
74 //Kruskal算法
75 Q=0;
76 for (i=0; i<=eageCount; i++){
77 va=sortDis[i]/MAX;
78 vb=sortDis[i]%MAX;
79 if (FindSet(v, va) != FindSet(v, vb)){
80 Union(v, va, vb);
81 Q+=distance[va][vb];
82 }
83 }
84 printf("The highest possible quality is 1/%d.\n", Q);
85 }
86 }
继续探索Prime中。。。
参考链接:http://www.5ushare.com/bbs/redirect.php?fid=5&tid=46&goto=nextoldset
隔了好长时间,又回来做最小生成树了,这次把这题很轻松的AC了,用了PRIM算法配合手工优先权队列。
啊,发现当时自己好小白啊。
下面是AC代码:
/*
=========================================
Prob :poj1789 Truck History
Type :最小生成树
Status:AC
Time :360MS
Memory:15636K
Author:myst
Remark:
==========================================
*/
#include
<
iostream
>
#define
SIZ 2010
#define
INF INT_MAX
using
namespace
std;
int
N;
int
map[SIZ][SIZ];
bool
visited[SIZ];
int
dis[SIZ];
//
-------------priority queue----------------------
int
heap_size;
int
heap[SIZ];
int
SWAP(
int
i,
int
j)
{
heap[i]
^=
heap[j];heap[j]
^=
heap[i];heap[i]
^=
heap[j];
return
0
;
}
int
MIN_HEAPIFY(
int
i)
{
int
l
=
i
<<
1
;
int
r
=
(i
<<
1
)
+
1
;
int
min;
if
(l
<=
heap_size
&&
dis[heap[l]]
<
dis[heap[i]]) min
=
l;
else
min
=
i;
if
(r
<=
heap_size
&&
dis[heap[r]]
<
dis[heap[min]]) min
=
r;
if
(min
!=
i){
SWAP(min, i);
MIN_HEAPIFY(min);
}
return
0
;
}
int
BUILD_HEAP()
{
for
(
int
i
=
heap_size
>>
1
;i
>=
1
;i
--
)
MIN_HEAPIFY(i);
return
0
;
}
int
EXTRACT_MIN()
{
int
min
=
heap[
1
];
SWAP(
1
, heap_size);
heap_size
--
;
return
min;
}
//
-------------------------------------------------
int
MST_PRIM()
{
int
ans
=
0
;
for
(
int
i
=
1
;i
<=
N;i
++
){
dis[i]
=
INF;
visited[i]
=
0
;
heap[i]
=
i;
}
dis[
1
]
=
0
;
heap_size
=
N;
BUILD_HEAP();
while
(heap_size
>
0
){
int
u
=
EXTRACT_MIN();
//
为减少操作,EXTRACT_MIN没有执行MIN_HEAPIFY,统一到下面BUILD_HEAP()一起执行。
visited[u]
=
1
;
ans
+=
dis[u];
for
(
int
j
=
1
;j
<=
N;j
++
){
if
(
!
visited[j]
&&
map[u][j]
&&
map[u][j]
<
dis[j])
dis[j]
=
map[u][j];
}
BUILD_HEAP();
}
return
ans;
}
int
main()
{
//
freopen("in.txt", "r", stdin);
char
code[SIZ][
10
];
while
(scanf(
"
%d
"
,
&
N)
&&
N){
for
(
int
i
=
1
;i
<=
N;i
++
)
scanf(
"
%s
"
, code[i]);
for
(
int
i
=
1
;i
<=
N
-
1
;i
++
){
for
(
int
j
=
i
+
1
;j
<=
N;j
++
){
int
dif
=
0
;
for
(
int
k
=
0
;k
<
7
;k
++
)
if
(code[i][k]
!=
code[j][k]) dif
++
;
map[i][j]
=
dif;
map[j][i]
=
dif;
}
}
printf(
"
The highest possible quality is 1/%d.\n
"
, MST_PRIM());
}
return
0
;
}
END