用一个7位的string代表一个编号,两个编号之间的距离代表这两个编号之间不同字母的个数。一个编号只能由另一个编号“衍生”出来,代价是这两个编号之间相应的距离,现在要找出一个“衍生”方案,使得总代价最小。
N<=2000;
´4个编号:
´aaaaaaa
´baaaaaa
´abaaaaa
´aabaaaa
后三个均由第一个衍生可得到最小距离和1+1+1=3.
1. 所有的“衍生方案”可表示为一幅图。
求一种“衍生方案”,实质上是去构造一棵树。
树的顶点代表编号,树的边权代表不同编号之间的衍生代价。
因为需要衍生出所有的方案而使总代价最小,所以要求最小生成树。
2.这是一幅稠密图,边数很多,所以选用邻接矩阵存储图、Prim算法实现最小生成树更优。
#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 999999
using namespace std;
int n,dis[2001][2001];
char s[2001][8];
int weight(int a, int b) { //返回两个编号的衍生代价
int sum = 0;
for (int i=0; i<7; i++)
if (s[a][i] != s[b][i]) sum++;
return sum;
}
int prim() {
bool book[2001]; //book数组表示点是否被选进了树中
int ans = 0, lowd[2001], Min, k, s = 1;
memset(lowd,inf,sizeof(lowd));
memset(book,false,sizeof(book));
book[s] = true;
for (int i=1; i<n; i++) {
Min = inf;
for (int j=2; j<=n; j++) {
if (!book[j] && lowd[j] > dis[s][j])
lowd[j] = dis[s][j];
if (!book[j] && Min > lowd[j]) {
Min = lowd[j];
k = j;
}
}
book[k] = true;
s = k; //s点不断更新
ans += Min;
}
return ans;
}
int main()
{
while (scanf("%d",&n) == 1 && n) {
for (int i=1; i<=n; i++) scanf("%s",s[i]);
for (int i=1; i<=n-1; i++) //注意这里i和j的循环范围,避免重复赋值
for (int j=i+1; j<=n; j++)
dis[i][j] = dis[j][i] = weight(i,j);
printf("The highest possible quality is 1/%d.\n",prim());
}
return 0;
}