POJ #1789 Truck History 最小生成树(MST) prim 稠密图 链式向前星

Description



  题目:链接

  这道题的数据集网上比较少,提供一组自己手写的数据:

INPUT:
3
aaaaaaa
baaaaaa
abaaaaa

OUTPUT:
The highest possible quality is 1/2.

思路



  题意比较不好理解,简而言之就是有 n 个字符串,设两个字符串之间的差异为 dis,dis 由两个字符串对应位置上不同字母的数量决定。比如串A“aaaaaaa” 、串B"baaaaaa" 和串C“abaaaaa”的 dis 分别为 dis(A, B) = 1, dis(A, C) = 1, dis(B, C) = 2 ...etc ,规定除其中一个串外其他串都是由该串派生的,那么产生三个计划:

  1、A派生B、C

  2、B派生A、C

  3、C派生A、B

  问所有计划中 dis 总和最小的计划是哪一个,输出 dis 总和。

  我们若以每个串为顶点,本题实质上就是求最小生成树的所有边权之和。

  很明显,建出的图是完全图,为稠密图,适合 prim 求解最小生成树。

  TLE N 次... 最后利用 char[] 代替 string 、堆优化prim、 链式向前星(静态邻接表,数组模拟链表)代替vector G[] ,最终1500+ms 险过。但是发现,其实动态申请效率也还可以,关键问题是出在更新d数组时重复入队过多,我(WTF???)

  当然解决 TLE 过程中也收获了很多,比如知道了堆优化的 prim 算法在处理稀疏图问题时居然比 kruskal 更快; 已知数据上界的情况下最好选用 char[] 当作string 和 链式向前星建图,因为vector动态申请非常耗时,push_back() 是成倍申请的,而 string 可能封装得过多了???(我猜的

  关于大牛对链式向前星的理解在这:链接

  测试证明:char[] 代替 string 少了 300+ms ,可怕...

  

  优化前版本

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<vector>
using namespace std;
#define INF 0x3f3f3f3f
const int MAXN = 2000 + 10;
int n;
string str[MAXN];

int getDis (int u, int v) {
    int cnt = 0;
    for (int i = 0; i <= 6; i++) {
        if (str[u][i] != str[v][i])
            cnt++;
    }
    return cnt;
}

struct Edge {
    int to;
    int dis;
    Edge(int tt, int dd) : to(tt), dis(dd) {}
};
vector<Edge> G[MAXN];

void addEdge (int u, int v) {
    G[u].push_back (Edge(v, getDis(u,v)));
}

void initG() {
    for (int i = 1; i <= n; i++) {
        G[i].clear();
        G[i].reserve(MAXN);
    }
    //建立完全图
    for (int i = 1; i <= n; i++) {
        for (int j = i+1; j <= n; j++) {
            addEdge(i, j);
            addEdge(j, i);
        }
    }
}

struct Node {
    int id;
    int key;
    Node (int v, int kk) : id(v), key(kk) {}
    friend bool operator < (const Node& a, const Node& b) {
        return a.key > b.key;
    }
};
int d[MAXN]; //连接v和树中结点的最小边的权值
bool vis[MAXN]; //v加入树
void prim (const int& s, int& sum ) {
    for (int i = 1; i <= n; i++) d[i] = INF, vis[i] = false;
    d[s] = 0;
    priority_queue<Node> pQueue;
    pQueue.push (Node(s, d[s]));
    while (!pQueue.empty()) {
        int u = pQueue.top().id; pQueue.pop();
        if (vis[u]) continue;
        vis[u] = true;
        sum += d[u]; //MST所有边的权重之和
        for (int j = 0; j < G[u].size(); j++) {
            int v = G[u][j].to;
            if (!vis[v] && d[v] > G[u][j].dis) {
                d[v] = G[u][j].dis;
                pQueue.push(Node(v, d[v]));
            }
        }
    }
}

int main(void) {
    while (cin >> n && n) {
        for (int i = 1; i <= n; i++) cin >> str[i];
        initG();
        int ans = 0;
        prim (1, ans);
        cout << "The highest possible quality is 1/" << ans << "." << endl;
    }
    return 0;
}

  优化后版本

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
#define INF 0x3f3f3f3f
const int MAXN = 2010;
const int EDGE_MAXN = 2010*2010;
int n;
char str[MAXN][7];

//求边权
int getDis (int u, int v) {
    int count = 0;
    for (int i = 0; i <= 6; i++) {
        if (str[u][i] != str[v][i]) count++;
    }
    return count;
}

//链式前向星(静态邻接表)
struct Edge {
    int to, dis, next;
}e[EDGE_MAXN];
int head[MAXN], cnt; //head存储以i为起点的最后一条边在e中的位置

void addEdge (int u, int v, int w) {
    cnt++; //从e[1]开始存边
    e[cnt].to = v;
    e[cnt].dis = w;
    e[cnt].next = head[u]; //next存储以u为起点的上一条边在e中的位置
    head[u] = cnt;
}

void initG() {
    memset(head, -1, sizeof(head));
    cnt = 0; //统计边数
    //建立完全图
    for (int i = 1; i <= n; i++) {
        for (int j = i+1; j <= n; j++) {
            int w = getDis(i, j);
            addEdge (i, j, w);
            addEdge (j, i, w);
        }
    }
}

//堆优化prim
struct Node {
    int id;
    int key;
    Node (int v, int kk) : id(v), key(kk) {}
    friend bool operator < (const Node& a, const Node& b) {
        return a.key > b.key;
    }
};
int d[MAXN]; //连接v和树中结点的最小边的权值
bool vis[MAXN]; //判断v是否加入树
void prim (const int& s, int& sum ) {
    memset(d, INF, sizeof(d));
    memset (vis, false, sizeof(vis));
    d[s] = 0;
    priority_queue<Node> pQueue;
    pQueue.push (Node(s, d[s]));
    while (!pQueue.empty()) {
        int u = pQueue.top().id; pQueue.pop();
        if (vis[u]) continue; //已加入树,不必再入树,避免TLE
        vis[u] = true;
        sum += d[u]; //MST所有边的权重之和
        //访问u的所有邻接点
        for (int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].to;
            if (!vis[v] && d[v] > e[i].dis) {
                d[v] = e[i].dis;
                pQueue.push(Node(v, d[v])); //和dijkstra一样存在重复入队的情况
            }
            /*
            if (!vis[v] ) {
                //d[v] = min (d[v], e[i].dis); 如此松驰将导致即使不更新d数组也入队,造成TLE
                pQueue.push(Node(v, d[v]));
            }
            */
        }
    }
}

int main(void) {
    while (cin >> n && n) {
        for (int i = 1; i <= n; i++) scanf("%s", str[i]);
        initG();
        int ans = 0;
        prim (1, ans);
        cout << "The highest possible quality is 1/" << ans << "." << endl;
    }
    return 0;
}

  prim 和 dijkstra 还真的很像,就是松弛不一样而已。

原文地址:https://www.cnblogs.com/Bw98blogs/p/8463396.html

时间: 2024-09-28 15:33:35

POJ #1789 Truck History 最小生成树(MST) prim 稠密图 链式向前星的相关文章

ZOJ 2158 &amp;&amp; POJ 1789 Truck History (经典MST)

链接:http://poj.org/problem?id=1789 或  http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1158 Description Advanced Cargo Movement, Ltd. uses trucks of different types. Some trucks are used for vegetable delivery, other for furniture, or for br

poj 1789 Truck History 最小生成树 prim

Truck History Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 19122   Accepted: 7366 Description Advanced Cargo Movement, Ltd. uses trucks of different types. Some trucks are used for vegetable delivery, other for furniture, or for brick

poj 1789 Truck History 最小生成树

Description Advanced Cargo Movement, Ltd. uses trucks of different types. Some trucks are used for vegetable delivery, other for furniture, or for bricks. The company has its own code describing each type of a truck. The code is simply a string of ex

Kuskal/Prim POJ 1789 Truck History

题目传送门 1 /* 2 题意:给出n个长度为7的字符串,一个字符串到另一个的距离为不同的字符数,问所有连通的最小代价是多少 3 Kuskal/Prim: 先用并查集做,简单好写,然而效率并不高,稠密图应该用Prim.这是最小生成数的裸题,然而题目有点坑爹:( 4 */ 5 #include <cstdio> 6 #include <cstring> 7 #include <string> 8 #include <algorithm> 9 #include

POJ 1789 -- Truck History(Prim)

 POJ 1789 -- Truck History Prim求分母的最小.即求最小生成树 1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn = 2000 + 10; 6 const int INF = 1000000; 7 int n;//有几个卡车 8 char str[maxn][10]; 9 int d[ma

poj 1789 Truck History 解题报告

题目链接:http://poj.org/problem?id=1789 题目意思:给出 N 行,每行7个字符你,统计所有的 行 与 行 之间的差值(就是相同位置下字母不相同),一个位置不相同就为1,依次累加.问最终的差值最少是多少. 额.....题意我是没看懂啦= =......看懂之后,就转化为最小生成树来做了.这是一个完全图,即每条边与除它之外的所有边都连通.边与边的权值是通过这个差值来算出来的. 1 #include <iostream> 2 #include <cstdio>

poj 1789 Truck History(kruskal算法)

题目链接:http://poj.org/problem?id=1789 思路:把每一行看成一个一个点,每两行之间不懂得字符个数就看做是权值.然后用kruskal算法计算出最小生成树 我写了两个代码一个是用优先队列写的,但是超时啦,不知道为什么,希望有人可以解答.后面用的数组sort排序然后才AC. code: 数组sort排序AC代码: #include<cstdio> #include<queue> #include<algorithm> #include<io

图论 ---- spfa + 链式向前星 ---- poj 3268 : Silver Cow Party

Silver Cow Party Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 12674   Accepted: 5651 Description One cow from each of N farms (1 ≤ N ≤ 1000) conveniently numbered 1..N is going to attend the big cow party to be held at farm #X (1 ≤ X 

图论 --- spfa + 链式向前星 : 判断是否存在正权回路 poj 1860 : Currency Exchange

Currency Exchange Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 19881   Accepted: 7114 Description Several currency exchange points are working in our city. Let us suppose that each point specializes in two particular currencies and pe