花了好长时间去理解题意,看来英语阅读能力还有待提高。
题意大概是这样的:用一个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版本:


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 ;
}