JOJ 1133 Domino Effect

 

题目链接: http://acm.jlu.edu.cn/joj/showproblem.php?pid=1133
可以这样简单理解这个题目:一个连通图,把标号为 1 的顶点点燃,所有边的燃烧速度相同,问整个图全部烧完需要多长时间。例如,下图:
JOJ 1133 Domino Effect_第1张图片 对于左图,从它的一个顶点开始点燃,每条边上的数字表示这条边从一个顶点燃烧到另一个顶点需要的时间,左图三条边烧完都需要 5 秒钟,所以整个图烧完需要 7.5 秒。
JOJ 1133 就是给定一个连通图,这个连通最多有 500 个顶点,以及给定每条边和这条边燃烧需要的时间,计算它燃烧完需要多少时间。
这个题目自己当时有两个思路,思路一称为时间戳引爆点队列算法。具体如下:设定一个时间步长 span ,设定一个队列存放当前正在燃烧的边,每隔 span 时 间检查一次当前燃烧到了什么地方,更新队列,当队列为空时,总时间就是这个图燃烧需要的时间。但这个算法实现起来太多细节,例如:需要对零头做特殊处理; 步长很难选定,选得太长,无法控制,这个算法就什么都不是了,选得太短,则遇到一条燃烧需要很长时间的边时会做大量无用功,一个想法是选择当前队列中剩余 时间最短的那条边的时间做步长;更新局面时比较困难,假如一条边两端都在燃烧则需要特殊加以标记,更新书面的复杂程序完全取决于步长的选择。
所以这只是个想法,可能会有结果,但不再继续考虑下去。
思路二:最短路径算法。自然燃烧必然走的是最短路径。使用最短路径算法在思想上的一个重 要突破点是以边为对象来考察。当所有的边都燃烧完时,整个图也就完了,总时间就是所有边燃烧完成时刻的最大值。对于一个顶点,燃烧到这个顶点的时间也就是 从起点到这个顶点的最短路径。假设有一条边 (u, v) ,用 dist(s, x) 表示从起点 s 到顶点 x 的最短路径, t(x, y) 表示边 (x, y) 从一个端点燃烧到另一个端点所用的时间。设 dist(s, u) = t1, dist(s, v) = t2 ,则有以下三种情况:
1)      t1 < t2 :此时边 (u, v) 燃烧完成的时间是: t1 + (t2 – t1) + ( t(u, v) – (t2 – t1) ) / 2
2)      t1 = t2 :此时边 (u, v) 燃烧完成的时间是: t1 + t(u, v) / 2
3)      t1 > t2 :此时情况与 1) 相反。
综合以上三种情况,边 (u, v) 燃烧完成的时间等于: max(t1, t2) + ( t(u, v) – delta ) / 2 ,其中 delta = | t1 – t2 |
到这里,剩下的工作非常简单,就是一个 dijkstra 算法。下面是 AC 的代码:
#define _CRT_SECURE_NO_DEPRECATE
 
#include <cstdio>
#include <cmath>
#include <vector>
#include <queue>
using namespace std;
 
 
struct Node {
     int dest, length;
     Node(int d, int l) {
         dest = d;
         length = l;
     }
     Node() {}
};
 
vector<vector<Node> > graph(510, vector<Node>());
int dist[510];
int nKeyDominoes, nRows;
 
void dijkstra() {
     int i;
     for (i = 1; i <= nKeyDominoes; i++) dist[i] = -1;
     dist[1] = 0;
 
     bool visited[510] = {false };
     queue<int > q;
     q.push(1);
     while (!q.empty()) {
         int head = q.front();
         q.pop();
 
         for (size_t i = 0; i < graph[head].size(); i++) {
              if (dist[graph[head][i].dest] == -1 ||
                   dist[graph[head][i].dest] > dist[head] + graph[head][i].length) {
                       dist[graph[head][i].dest] = dist[head] + graph[head][i].length;
              }
         }
         visited[head] = true ;
         int minimal = -1, inf = 1 << 30;
         for (int i = 1; i <= nKeyDominoes; i++) {
              if (dist[i] != -1 && !visited[i]) {
                   if (dist[i] < inf) {
                       inf = dist[i];
                       minimal = i;
                   }
              }
         }
         if (minimal != -1) q.push(minimal);
     }
}
 
int main() {
     int nSystem;
     freopen("in.txt" , "r" , stdin);
     scanf("%d" , &nSystem);
 
     for (int iSystem = 1; iSystem <= nSystem; iSystem++) {
         printf("System #%d/n" , iSystem);
 
         scanf("%d%d" , &nKeyDominoes, &nRows);
         for (int i = 0; i < nRows; i++) {
              int a, b, c;
              scanf("%d%d%d" , &a, &b, &c);
              graph[a].push_back(Node(b, c));
              graph[b].push_back(Node(a, c));
         }
        
         // compute the length of the shortest path from node 1 to other nodes
         dijkstra();
 
         double totalTime = 0.0;
         for (int i = 1; i <= nKeyDominoes; i++) {
              double start = dist[i];
              for (size_t j = 0; j < graph[i].size(); j++) {
                   double dest = dist[graph[i][j].dest];
                   double delta = fabs(dest - start);
                   double tmp = max(start, dest) + (graph[i][j].length - delta) / 2;
                   totalTime = max(tmp, totalTime);
              }
         }
         printf("The last domino falls after %.1lf seconds./n/n" , totalTime);
         for (int i = 1; i <= nKeyDominoes; i++) {
              graph[i].clear();
         }
     }
     return 0;
}

你可能感兴趣的:(算法,struct,vector,System,Graph,Path)