PAT甲级题分类汇编——图

本文为PAT甲级分类汇编系列文章。html

 

图,就是层序遍历和Dijkstra这一套,#include<queue> 是必须的。node

题号 标题 分数 大意 时间
1072 Gas Station 30 最短距离最大,距离不超限 200ms
1076 Forwards on Weibo 30 必定层数的转发计数 3000ms
1079 Total Sales of Supply Chain 25 计算供应链总销售额 250ms
1087 All Roads Lead to Rome 30 复杂权重的最短路径问题 200ms
1090 Highest Price in Supply Chain 25 供应链最高价格 200ms
1091 Acute Stroke 30 超过阈值的空间体积之和 600ms

图这一类题的分值都比较大,由于代码量大,同时时间要么短要么长,不是标准的400ms。至于3000ms的1076,是一道简单题,应该是出题老师一不当心多打了一个0。ios

30分题确定要作,此次就只作4道30分题:107二、107六、1087和1091。算法

 

1072:app

这道题的输入数据是很典型的,算法以Dijkstra为核心,以后再加上一点最小距离、范围、平均距离等简单的东西,不难。spa

不知是第几回写Dijkstra了,每次实现都不太同样。以前写过一篇Dijkstra算法与堆,如今看来,算法都不是很好。这一次通过深思熟虑后,我将下面这种写法固定下来:code

  1 #include <iostream>
  2 #include <iomanip>
  3 #include <sstream>
  4 #include <vector>
  5 #include <queue>
  6 #include <limits>
  7 
  8 struct Road
  9 {
 10     Road(int dest, int dist)
 11         : dest(dest), dist(dist) { }
 12     int dest;
 13     int dist;
 14 };
 15 
 16 struct Node
 17 {
 18     int dist = std::numeric_limits<int>::max();
 19     std::vector<Road> roads;
 20 };
 21 
 22 struct Coll
 23 {
 24     Coll(int index, int dist)
 25         : index(index), dist(dist) { }
 26     int index;
 27     int dist;
 28 };
 29 
 30 struct Comp
 31 {
 32     static void init(std::vector<Node>& _nodes)
 33     {
 34         nodes = &_nodes;
 35     }
 36     static std::vector<Node>* nodes;
 37     bool operator()(Coll i, Coll j)
 38     {
 39         return (*nodes)[i.index].dist > (*nodes)[j.index].dist;
 40     }
 41 };
 42 
 43 std::vector<Node>* Comp::nodes = nullptr;
 44 
 45 int get_node(int _offset)
 46 {
 47     std::string s;
 48     std::stringstream ss;
 49     std::cin >> s;
 50     ss << s;
 51     if (s[0] == 'G')
 52     {
 53         ss.get();
 54         int i;
 55         ss >> i;
 56         --i;
 57         i += _offset;
 58         return i;
 59     }
 60     else
 61     {
 62         int i;
 63         ss >> i;
 64         --i;
 65         return i;
 66     }
 67 }
 68 
 69 int main()
 70 {
 71     int num_house, num_station, num_road, range;
 72     std::cin >> num_house >> num_station >> num_road >> range;
 73     std::vector<Node> nodes(num_house + num_station);
 74     Comp::init(nodes);
 75     for (int i = 0; i != num_road; ++i)
 76     {
 77         auto n0 = get_node(num_house), n1 = get_node(num_house);
 78         int dist;
 79         std::cin >> dist;
 80         nodes[n0].roads.emplace_back(n1, dist);
 81         nodes[n1].roads.emplace_back(n0, dist);
 82     }
 83     int best = -1;
 84     int minimum = 0;
 85     int total = std::numeric_limits<int>::max();
 86     auto house_begin = nodes.begin();
 87     auto house_end = nodes.begin() + num_house;
 88     auto station_begin = nodes.begin() + num_house;
 89     auto station_end = nodes.end();
 90     for (auto station = station_begin; station != station_end; ++station)
 91     {
 92         std::priority_queue<Coll, std::vector<Coll>, Comp> queue;
 93         for (auto& node : nodes)
 94             node.dist = std::numeric_limits<int>::max();
 95         station->dist = 0;
 96         queue.emplace(station - nodes.begin(), 0);
 97         while (!queue.empty())
 98         {
 99             auto coll = queue.top();
100             queue.pop();
101             auto& node = nodes[coll.index];
102             if (node.dist != coll.dist)
103                 continue;
104             for (const auto& r : node.roads)
105             {
106                 if (node.dist + r.dist < nodes[r.dest].dist)
107                 {
108                     nodes[r.dest].dist = node.dist + r.dist;
109                     queue.emplace(r.dest, nodes[r.dest].dist);
110                 }
111             }
112         }
113         int m = std::numeric_limits<int>::max();
114         int t = 0;
115         bool bad = false;
116         for (auto house = house_begin; house != house_end; ++house)
117         {
118             if (house->dist > range)
119                 bad = true;
120             t += house->dist;
121             if (house->dist < m)
122                 m = house->dist;
123         }
124         if (bad)
125             continue;
126         if (m > minimum)
127         {
128             minimum = m;
129             best = station - nodes.begin();
130             total = t;
131         }
132         else if (m == minimum && t < total)
133         {
134             best = station - nodes.begin();
135             total = t;
136         }
137     }
138     if (best == -1)
139     {
140         std::cout << "No Solution";
141         return 0;
142     }
143     best = best - num_house + 1;
144     std::cout << 'G' << best << std::endl;
145     std::cout.setf(std::ios::fixed);
146     std::cout << std::setprecision(1);
147     std::cout << (double)minimum << ' ' << (double)total / num_house;
148 }

Dijkstra算法的时间复杂度关键取决于“找到距离已收集顶点最近的顶点”这步操做的复杂度。线性确定是很差的,用优先队列是更好的方法。然而,当你把一个顶点加入到队列中之后,在它弹出以前,这个顶点的距离可能变得更近,即会改变它在堆中应该放的位置,为此须要重排,这样时间复杂度就不划算了。个人新方法是,在优先队列中存储结构体,包括顶点下标与插入优先队列时它的距离。弹出时,将这个距离与顶点对象中维护的距离相比较,若是不相等,说明这个弹出已通过时了,只是以前受限于结构没法删除,但如今能够忽略。因为优先队列以结构体中的距离为键值,此顶点在以前确定已经被处理过了,因此能够名正言顺地忽略它。这样就能够不须要排序、查找等乱七八糟操做,同时顶点对象中也再也不须要表示是否已收集的变量了。htm

 

1087:对象

在Dijkstra算法的基础上,这道题主要增长了对考虑相等状况的要求。在cost同为最小的状况下,看第二键值happiness,在后者同为最大的状况下,选择更大的平均happiness,也就是看路径长度哪一个更短。因为最优的选择条件依赖于整条路径,不能由已收集的部分得出,所以必须维护全部可能的路径,跟以前PBMC的题相似,我记得那道题卡了我很久。blog

维护路径的过程当中,若是出现相等长度的状况(这道题中是cost),路径要合并;若是有更短路径,就不要以前维护的路径,而从当前处理的节点的路径生成。

  1 #include <iostream>
  2 #include <string>
  3 #include <vector>
  4 #include <queue>
  5 #include <map>
  6 #include <limits>
  7 
  8 struct Route
  9 {
 10     Route() = default;
 11     Route(int dest, int dist)
 12         : dest(dest), dist(dist) { }
 13     int dest;
 14     int dist;
 15 };
 16 
 17 struct City
 18 {
 19     std::string name;
 20     std::vector<Route> routes;
 21     int happiness;
 22     int dist = std::numeric_limits<int>::max();
 23     std::vector<std::vector<int>> paths;
 24 };
 25 
 26 struct Collect
 27 {
 28     Collect(int dest, int dist)
 29         : dest(dest), dist(dist) { }
 30     int dest;
 31     int dist;
 32 };
 33 
 34 struct Comparator
 35 {
 36     static void init(std::vector<City>& _cities)
 37     {
 38         cities = &_cities;
 39     }
 40     static std::vector<City>* cities;
 41     bool operator()(const Collect& lhs, const Collect& rhs)
 42     {
 43         return (*cities)[lhs.dest].dist > (*cities)[rhs.dest].dist;
 44     }
 45 };
 46 
 47 std::vector<City>* Comparator::cities = nullptr;
 48 
 49 int main()
 50 {
 51     int num_cities, num_routes;
 52     std::cin >> num_cities >> num_routes;
 53     std::vector<City> cities(num_cities);
 54     std::cin >> cities[0].name;
 55     std::map<std::string, int> city_map;
 56     for (int i = 1; i != num_cities; ++i)
 57     {
 58         std::cin >> cities[i].name;
 59         std::cin >> cities[i].happiness;
 60         city_map[cities[i].name] = i;
 61     }
 62     for (int i = 0; i != num_routes; ++i)
 63     {
 64         std::string n0, n1;
 65         std::cin >> n0 >> n1;
 66         auto i0 = city_map[n0], i1 = city_map[n1];
 67         int dist;
 68         std::cin >> dist;
 69         cities[i0].routes.emplace_back(i1, dist);
 70         cities[i1].routes.emplace_back(i0, dist);
 71     }
 72     Comparator::init(cities);
 73     cities[0].dist = 0;
 74     cities[0].paths.resize(1);
 75     std::priority_queue<Collect, std::vector<Collect>, Comparator> queue;
 76     queue.emplace(0, 0);
 77     while (!queue.empty())
 78     {
 79         auto col = queue.top();
 80         queue.pop();
 81         auto& city = cities[col.dest];
 82         if (city.name == "ROM")
 83             break;
 84         if (city.dist != col.dist)
 85             continue;
 86         for (const auto& r : city.routes)
 87         {
 88             auto& target = cities[r.dest];
 89             if (city.dist + r.dist < target.dist)
 90             {
 91                 target.dist = city.dist + r.dist;
 92                 target.paths = city.paths;
 93                 for (auto& p : target.paths)
 94                     p.push_back(col.dest);
 95                 queue.emplace(r.dest, target.dist);
 96             }
 97             else if (city.dist + r.dist == target.dist)
 98             {
 99                 for (auto p : city.paths)
100                 {
101                     p.push_back(col.dest);
102                     target.paths.push_back(std::move(p));
103                 }
104             }
105         }
106     }
107     auto& rome = cities[city_map["ROM"]];
108     std::cout << rome.paths.size() << ' ' << rome.dist << ' ';
109     int happiness = 0;
110     int count = 0;
111     int recommended;
112     for (auto p = rome.paths.begin(); p != rome.paths.end(); ++p)
113     {
114         int h = 0;
115         for (int i : *p)
116             h += cities[i].happiness;
117         if (h > happiness)
118         {
119             happiness = h;
120             count = p->size();
121             recommended = p - rome.paths.begin();
122         }
123         else if (h == happiness && p->size() < count)
124         {
125             count = p->size();
126             recommended = p - rome.paths.begin();
127         }
128     }
129     happiness += rome.happiness;
130     std::cout << happiness << ' ' << happiness / (count ? count : 1) << std::endl;
131     for (int i : rome.paths[recommended])
132         std::cout << cities[i].name << "->";
133     std::cout << "ROM" << std::endl;
134 }

第一次提交的时候,一个case出现浮点错误。我没有用过浮点,那就是除以0的错误。这个case确定是一种边界状况,可是我不怎么清楚路径长度是怎么为0的。也许是出发点就是终点吧。

 

1076:

3000ms真的吓到我了,啥题须要3000ms呢?怀着忐忑的心情我作完了这道题,跑出来140ms,并且我用的是C++的输入输出。所以我有理由相信这道题本应是300ms。

这里的图是没有权重的,所以就是一个简单的层序遍历,只是要维护层数并判断。把层数放入对象,像Dijkstra算法中维护距离同样,在处理一个对象时把后继节点的层数设为此对象的层数加1就能够了。

 1 #include <iostream>
 2 #include <vector>
 3 #include <queue>
 4 
 5 struct User
 6 {
 7     std::vector<int> fans;
 8     int layer;
 9     bool forward;
10 };
11 
12 int main()
13 {
14     int num_user, layer;
15     std::cin >> num_user >> layer;
16     std::vector<User> users(num_user);
17     for (int i = 0; i != num_user; ++i)
18     {
19         auto& u = users[i];
20         int count;
21         std::cin >> count;
22         for (int j = 0; j != count; ++j)
23         {
24             int t;
25             std::cin >> t;
26             --t;
27             users[t].fans.push_back(i);
28         }
29     }
30     int num_query;
31     std::cin >> num_query;
32     for (int i = 0; i != num_query; ++i)
33     {
34         int t;
35         std::cin >> t;
36         --t;
37         for (auto& u : users)
38             u.layer = 0, u.forward = false;
39         users[t].forward = true;
40         int count = 0;
41         std::queue<int> queue;
42         queue.push(t);
43         while (!queue.empty())
44         {
45             auto index = queue.front();
46             auto& user = users[index];
47             queue.pop();
48             for (int i : user.fans)
49             {
50                 auto& fan = users[i];
51                 if (!fan.forward)
52                 {
53                     //std::cout << i << ' ';
54                     fan.forward = true;
55                     ++count;
56                     fan.layer = user.layer + 1;
57                     if (fan.layer < layer)
58                         queue.push(i);
59                 }
60             }
61         }
62         std::cout << count << std::endl;
63     }
64 }

算法很正确,可是样例就错了。把转发的用户打印出来看,我发现最初发微博的用户做为本身的粉丝的粉丝……的粉丝,把本身的微博转发了一下。因而我就在初始条件里面把这个用户设置为已转发,但不计数。要是样例输入不挖这个坑,我可能要好久才能发现这个问题。

 

1091:

给定立体图像,计算每一块的体积并选择达到阈值的进行累加。

对于一个有效单位,能够用像层序遍历同样的算法把与之连通的单位都计算进来,同时将它们都标记为以收集。碰到下一个未收集的有效单位,再用一样的方法计算,直到遍历完。

实现中还用到了昨天提到的内部迭代器,用统一的接口包装输入和计算两个看似绝不相关的操做。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <queue>
  4 
  5 struct Unit
  6 {
  7     bool bad;
  8     bool collected = false;
  9 };
 10 
 11 struct Point
 12 {
 13     Point()
 14         : Point(0, 0, 0) { }
 15     Point(int x, int y, int z)
 16         : x(x), y(y), z(z) { }
 17     int x;
 18     int y;
 19     int z;
 20 };
 21 
 22 class Brain
 23 {
 24 public:
 25     Brain(Point p)
 26         : size(p)
 27     {
 28         data = new Unit[size.x * size.y * size.z];
 29     }
 30     ~Brain()
 31     {
 32         delete[] data;
 33     }
 34     Brain(const Brain&) = delete;
 35     Brain(Brain&&) = delete;
 36     Brain& operator=(const Brain&) = delete;
 37     Brain& operator=(Brain&&) = delete;
 38     Unit& operator[](const Point& p)
 39     {
 40         return data[point_to_int(p)];
 41     }
 42     template <typename F>
 43     void for_each(F _functor)
 44     {
 45         Point p;
 46         for (p.x = 0; p.x != size.x; ++p.x)
 47             for (p.y = 0; p.y != size.y; ++p.y)
 48                 for (p.z = 0; p.z != size.z; ++p.z)
 49                     _functor(p, operator[](p));
 50     }
 51     Point size;
 52 private:
 53     Unit* data;
 54     int point_to_int(const Point& p)
 55     {
 56         return (p.x * size.y + p.y) * size.z + p.z;
 57     }
 58 };
 59 
 60 void push_if_valid(std::queue<Point>& queue, Brain& brain, const Point& point)
 61 {
 62     if (point.x >= 0 && point.x < brain.size.x &&
 63         point.y >= 0 && point.y < brain.size.y &&
 64         point.z >= 0 && point.z < brain.size.z &&
 65         brain[point].bad &&
 66         !brain[point].collected)
 67         queue.emplace(point);
 68 }
 69 
 70 void push_if_valid(std::queue<Point>& queue, Brain& brain, int x, int y, int z)
 71 {
 72     if (x >= 0 && x < brain.size.x &&
 73         y >= 0 && y < brain.size.y &&
 74         z >= 0 && z < brain.size.z)
 75     {
 76         Point p(x, y, z);
 77         auto& unit = brain[p];
 78         if (unit.bad && !unit.collected)
 79             queue.emplace(std::move(p));
 80     }
 81         
 82 }
 83 
 84 int main()
 85 {
 86     int size_x, size_y, size_z, threshold;
 87     std::cin >> size_y >> size_z >> size_x >> threshold;
 88     Point size(size_x, size_y, size_z);
 89     Brain brain(size);
 90     brain.for_each([](const Point& p, Unit& u) {
 91         //std::cin >> u.bad;
 92         int t;
 93         std::scanf("%d", &t);
 94         u.bad = t;
 95     });
 96     int total = 0;
 97     brain.for_each([=, &brain, &total](const Point& p, Unit& u) {
 98         if (!u.bad || u.collected)
 99             return;
100         int size = 0;
101         std::queue<Point> queue;
102         queue.push(p);
103         while (!queue.empty())
104         {
105             auto p = queue.front();
106             queue.pop();
107             auto& unit = brain[p];
108             if (!unit.bad || unit.collected)
109                 continue;
110             unit.collected = true;
111             ++size;
112             push_if_valid(queue, brain, p.x - 1, p.y, p.z);
113             push_if_valid(queue, brain, p.x + 1, p.y, p.z);
114             push_if_valid(queue, brain, p.x, p.y - 1, p.z);
115             push_if_valid(queue, brain, p.x, p.y + 1, p.z);
116             push_if_valid(queue, brain, p.x, p.y, p.z - 1);
117             push_if_valid(queue, brain, p.x, p.y, p.z + 1);
118         }
119         if (size >= threshold)
120             total += size;
121     });
122     std::cout << total;
123 }

然而,这道题有个坑,在于输入数据第一行中3个维度的长度不是x、y、z的顺序,而是y、z、x的顺序。若是顺序不对,那么后面判断连通也会错误。若是按x、y、z来处理,样例数据的输出会是28而不是26,除了那大片区域外的4个点连通起来了。

相关文章
相关标签/搜索