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個點連通起來了。

相關文章
相關標籤/搜索