POJ_1789_Truck History (最小生成树Prim算法)
题意大概是这样的:用一个7位的string代表一个编号,两个编号之间的distance代表这两个编号之间不同字母的个数。一个编号只能由另一个编号“衍生”出来,代价是这两个编号之间相应的distance,现在要找出一个“衍生”方案,使得总代价最小,也就是distance之和最小。
例如有如下4个编号:
aaaaaaa
baaaaaa
abaaaaa
aabaaaa
显然的,第二,第三和第四编号分别从第一编号衍生出来的代价最小,因为第二,第三和第四编号分别与第一编号只有一个字母是不同的,相应的distance都是1,加起来是3。也就是最小代价为3。
问题可以转化为最小代价生成树的问题。因为每两个结点之间都有路径,所以是完全图。
此题的关键是将问题转化为最小生成树的问题。每一个编号为图的一个顶点,顶点与顶点间的编号差即为这条边的权值,题目所要的就是我们求出最小生成树来。这里我用prim算法来求最小生成树。
/*
可能会有疑问的是:每次都是从新的源点出发找最小的,怎么能保证已经加入最小生成树的点周围没有最小的呢
其时,prim算法在从第一个源点开始,就已经记录了他到每一个可达点的最短距离,如果后面的其他源点还有
最小的那么就会更新,否则是不会更新的。
*/
1 # include <stdio.h> 2 # include <string.h> 3 int map[2011][2011]={0}; 4 int visit[100]; 5 int n; 6 const int inf=10; 7 char str[2011][8]; 8 int search(int i,int j) 9 { 10 int sum=0; 11 for(int k=0;k<7;k++) 12 if(str[i][k] != str[j][k]) 13 sum++; 14 return sum; 15 } 16 int prim() 17 { 18 int s=1; //记录找最短路径时所经过的点 19 int m=1; //记录最小生成树的顶点数。 20 int prim_w=0; //最小生成树的总权值即所要找的最短路径。 21 int min_w; //标记每个新源点到其他点的最短路。 22 int point; 23 int low_map[2011]; //用于记录各源点到其他没标记过的点的最短路。 24 bool u[2011]; //标记某顶点是否属于最小生成树。 25 memset(low_map,inf,sizeof(low_map)); 26 memset(u,0,sizeof(u)); 27 u[s]=1; 28 while(1) 29 { 30 if(m==n)break; 31 min_w=inf; 32 for(int j=2;j<=n;j++) 33 { 34 if(!u[j] && low_map[j]>map[s][j]) 35 low_map[j]=map[s][j]; 36 if(!u[j] && min_w>low_map[j]) 37 { 38 min_w=low_map[j]; //如果循环一次没有找到最小的,那么就把上一个 39 //源点记录的最小值赋值给min_w; 40 point=j; 41 } 42 } 43 s=point; 44 u[s]=1; 45 prim_w+=min_w; 46 m++; 47 } 48 return prim_w; 49 } 50 int main() 51 { 52 int i,j; 53 while(scanf("%d",&n)!=EOF) 54 { 55 if(n==0)break; 56 for(i=1;i<=n;i++) 57 scanf("%s",str[i]); 58 for(i=1;i<=n-1;i++) 59 { 60 for(j=i+1;j<=n;j++) 61 map[i][j]=map[j][i]=search(i,j); 62 } 63 printf("The highest possible quality is 1/%d.\n",prim()); 64 } 65 66 }