最小生成树--Truck History(poj 1789);

卡车历史
时限:2000MS 内存限制:65536K
提交材料共计: 30032 接受: 11735
描述


先进货运有限公司使用不同类型的卡车。有些卡车用于蔬菜运送,其他用于家具或砖。该公司有自己的代码描述每种类型的卡车。该代码只是一个由七个小写字母组成的字符串(每个位置上的每个字母都有一个非常特殊的含义,但对于这个任务并不重要)。在公司发展的初期,只使用了一种卡车类型,后来又衍生出其他类型的卡车,然后从新的类型中衍生出另一种类型,等等。


今天,ACM已经足够富有,足以让历史学家们去研究它的历史了。有一件事是历史学家试图找出的,那就是所谓的派生计划-即如何派生出卡车类型。他们把卡车类型的距离定义为卡车类型代码中不同字母的位置数。他们还认为,每种卡车类型都是从另外一辆卡车中派生出来的(第一种卡车类型除外,它不是从任何其他类型衍生出来的)。然后将派生计划的质量定义为
                               1/Σ(to,td)d(to,td)


其中,求和在派生计划中对所有类型的类型进行处理,使tO是原始类型和td派生自它和d(T0,td)的类型是类型的距离。
因为历史学家失败了,你要写一个计划来帮助他们。给定卡车类型的代码,您的程序应该找到派生计划的最高质量。
输入


输入由几个测试用例组成。每个测试用例以一个包含卡车类型数量的直线开始,n,2<=n<=2 000。下面的每个输入行都包含一个卡车类型代码(一个由七个小写字母组成的字符串)。您可以假设代码唯一地描述了卡车,也就是说,这些N中没有两个是相同的。输入在卡车类型的数目处以零结束。
输出


对于每个测试用例,您的程序应该输出文本“ The highest possible quality is 1/q”,其中1/q是最佳派生计划的质量。
样本输入


4
aaaaaaa
baaaaaa
abaaaaa

aabaaaa

 0

样本输出


The highest possible quality is 1/3.

题意大概是这样的:用一个7位的string代表一个编号,两个编号之间的distance代表这两个编号之间不同字母的个数。一个编号只能由另一个编号衍生出来,代价是这两个编号之间相应的distance,现在要找出一个衍生方案,使得总代价最小,也就是distance之和最小。

例如有如下4个编号:

aaaaaaa

baaaaaa

abaaaaa

aabaaaa

显然的,第二,第三和第四编号分别从第一编号衍生出来的代价最小,因为第二,第三和第四编号分别与第一编号只有一个字母是不同的,相应的distance都是1,加起来是3。也就是最小代价为3

问题可以转化为最小代价生成树的问题。因为每两个结点之间都有路径,所以是完全图。 

此题的关键是将问题转化为最小生成树的问题。每一个编号为图的一个顶点,顶点与顶点间的编号差即为这条边的权值,题目所要的就是我们求出最小生成树来。这里我用prim算法来求最小生成树。


#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#define NUM 2002

using namespace std;
int Graph[NUM][NUM];
int N;//顶点数;
char str[NUM][8];




struct node     //保存key值的结点
{
    int v;
    int key;
    friend bool operator<(node a, node b)   //自定义优先级,key小的优先
    {
    return a.key > b.key;
    }
};


int parent[NUM];   //每个结点的父节点
bool visited[NUM]; //是否已经加入树种


node vx[NUM];      //保存每个结点与其父节点连接边的权值
priority_queue<node> q; //优先队列stl实现、、将排序的功能用其来实现;
//将自动的按照相应的排序方式排序



void Prim()
{
    for(int i = 1; i <= N; i++) //初始化
        {
        vx[i].v = i;
        vx[i].key = 1<<30;
        parent[i] = -1;
        visited[i] = false;
        }


    vx[1].key = 0;
    q.push(vx[1]);



    while(!q.empty())
        {
        node nd = q.top();  //取队首,记得赶紧pop掉
        q.pop();



        if(visited[nd.v] == true)   //深意,因为push机器的可能是重复但是权值不同的点,我们只取最小的
            continue;
        int st = nd.v;
        visited[nd.v] = true;



        for(int j = 1;  j <= N; j++)
            {
            if(j!=st && !visited[j] && Graph[st][j] < vx[j].key)    //判断
                {
                parent[j] = st;
                vx[j].key = Graph[st][j];
                q.push(vx[j]);
                }
            }
        }
}




int main()
{
    while(scanf("%d",&N)&&N!=0){
        for(int i = 1;i<=N;i++)
            scanf("%s",str[i]);
        memset(Graph, 124,sizeof(Graph));
        for(int  i= 1;i<=N;i++)
            for(int j = i+1;j<=N;j++){
                int w = 0;
                for(int k = 0;k<7;k++)
                    if(str[i][k]!=str[j][k])
                        w++;
                Graph[j][i] = Graph[i][j] = w;

            }
        Prim();
        int ans = 0;
        for(int i = 1;i<=N;i++)
            ans+=vx[i].key;

        printf("The highest possible quality is 1/%d.\n",ans);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值