问题 D: 调查黑暗气息
时间限制: 1 Sec
内存限制: 128 MB
提交: 1120
解决: 148
提交 状态
题目描述
在数码世界中有一个叫做“Radiation Zone”的区域,里面荒无人烟,仿佛遗迹一般。在这个区域中有N个城市(假设编号为从0到N-1),每个城市中都有一定数量的辐能。有M条已知长度的道路连接它们,每条道路都可以双向来往。
近期这个区域似有黑暗气息蛰伏,国王Shoutmon派出调查队前来调查这个区域中的城市。调查队的飞船降落在S号城市。由于飞船降落时气流不稳定,因此产生了辐能波,导致以S号城市为中心的L层以内(假设S号城市为最内层,记为第0层)的城市的辐能都会上升(只上升一次),上升的数值为 “城市的当前辐能乘以百分比p”的向上取整。其中百分比p在S号城市时为100%,且每向外扩散一层,百分比降低100%/L(例如,如果L为5,那么第0层(即S号城市)为100%,第1层为80%,第2层为60%,第3层为40%,第4层为20%,其中百分比均为浮点数)。所谓第X层是指,连接某城市与S号城市的最少数量的道路数,例如下图是一个例子,图中的数字为其层号。
之后调查队需要前往T号城市调查。为了顺便清除城市中的辐能,他们准备了一个容量为K的辐能吸收器。辐能吸收器可以自动吸收城市中的辐能,且满容量时会自动将容器内的所有辐能都燃烧完毕,以继续吸收辐能。假设调查队总是把城市(含S号和T号城市)中的辐能吸收完毕。
为了节省体力,调查队希望选择一条长度最短的路径前往T号城市;如果这样的路径有多条,那么从中选择到达T号城市时辐能吸收器内当前辐能最大的路径;如果这样的路径仍然有多条,那么从中选择路径后半段的城市的辐能之和最小的路径(所谓后半段是指,如果路径上有m个城市,那么后m/2个城市(含T号城市)是后半段的城市。例如,如果路径上有7个城市,那么路径的后3个城市(除法为向下取整)为后半段的城市)。数据保证这样的路径一定唯一。
输入
每个输入文件中一组数据。
第一行六个整数N、M、L、K、S、T(2<=N<=500, M<=N*(N-1)/2, 1<=L<=500, 2<=K<=100, S != T),分别代表城市个数、道路条数、辐能上升的层数、辐能吸收器的容量、起点城市编号、终点城市编号。
接下来一行有N个正整数,分别给出N个城市的初始辐能(均为不超过100的正整数)。
接下来M行,每行三个数字u、v、w,代表一条道路,其中u和v为道路的两个端点城市编号,w为道路的长度(w为不超过1000的正整数)。数据保证u不等于v,且相同的无序对(u,v)只出现一次。
输出
如果从S号城市不能到达T号城市,那么只输出-1。
如果从S号城市能到达T号城市,那么输出两行:
第一行输出四个整数, 即S号城市到T号城市的最短距离的路径条数(数据保证不超过100000条)、S号城市到T号城市的最短距离、通过最终选择的路径到达T号城市时辐能吸收器内的当前辐能、最终选择的路径的后半段城市的辐能之和。
第二行输出最终选择的路径,路径上的城市之间用->隔开。
样例输入
7 8 1 7 0 6
20 10 10 6 8 13 5
0 1 1
0 2 1
1 3 1
2 4 1
2 5 1
3 6 1
4 6 1
5 6 1
样例输出
3 3 5 11
0->1->3->6
提示
本题的代码量相对大一些,但是思路还是很简单的。先用 BFS 计算各个顶点需要增加的辐
能,然后用 Dijkstra 求出所有最短路径,接着用 DFS 枚举所有最短路径,求出各数据最优值。
容易坑到的地方:
(1) 增加的辐能是要向上取整的。
(2) 图不连通没关系,但是起点到终点一定要连通。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int MAXV = 510; //最大顶点数
const int INF = 1000000000; //无穷大
//n为顶点数,m为边数,G为邻接矩阵,weight为点权
//d[]记录最短距离,numPath记录最短路径条数,maxModW记录最大模,minHalfW记录最小后半段点权之和
int n, m, L, K, S, T, G[MAXV][MAXV], weight[MAXV];
int d[MAXV], numPath = 0, maxModW = -1, minHalfW = INF;
bool vis[MAXV] = {false}; //vis[i]==true表示顶点i已访问,初始均为false
bool inq[MAXV] = {false};
vector<int> pre[MAXV]; //前驱
vector<int> tempPath, path; //临时路径、最优路径
void init() {
fill(G[0], G[0] + MAXV * MAXV, INF); //初始化图G
numPath = 0;
maxModW = -1;
minHalfW = INF;
memset(vis, false, sizeof(vis));
memset(inq, false, sizeof(inq));
for(int i = 0; i < MAXV; i++) {
pre[i].clear();
}
tempPath.clear();
path.clear();
}
struct Node {
int id; //结点编号
int layer; //结点层号
};
void BFS() {
queue<Node> q;
Node start;
start.id = S;
start.layer = 0;
q.push(start);
inq[start.id] = true;
while(!q.empty()) {
Node topNode = q.front();
q.pop();
int u = topNode.id;
if(topNode.layer < L) {
weight[u] += (int)(ceil(weight[u] * 1.0 * (L - topNode.layer) / L) + 0.5);
}
for(int v = 0; v < n; v++) {
if(!inq[v] && G[u][v] != INF) {
Node next;
next.id = v;
next.layer = topNode.layer + 1;
q.push(next);
inq[next.id] = true;
}
}
}
}
void Dijkstra() { //s为起点
fill(d, d + MAXV, INF);
for(int i = 0; i < n; i++) {
pre[i].push_back(i); //初始化pre数组
}
d[S] = 0;
for(int i = 0; i < n; i++) { //循环n次
int u = -1, MIN = INF; //u使d[u]最小,MIN存放该最小的d[u]
for(int j = 0; j < n; j++) { //找到未访问的顶点中d[]最小的
if(vis[j] == false && d[j] < MIN) {
u = j;
MIN = d[j];
}
}
vis[u] = true; //标记u为已访问
for(int v = 0; v < n; v++) {
//如果v未访问 && u能到达v
if(vis[v] == false && G[u][v] != INF) {
if(d[u] + G[u][v] < d[v]) { //以u为中介点使d[v]更小
d[v] = d[u] + G[u][v]; //优化d[v]
pre[v].clear(); //清空pre[v]
pre[v].push_back(u); //u为v的前驱
} else if(d[u] + G[u][v] == d[v]) { //找到相同长度的路径
pre[v].push_back(u); //u为v的前驱之一
}
}
}
}
}
void DFS(int v) { //v为当前结点
if(v == S) { //递归边界,到达叶子结点(路径起点)
tempPath.push_back(v);
numPath++;
int tempW = 0, tempHalfW = 0; //临时路径tempPath的点权之和
for(int i = 0; i < tempPath.size() / 2; i++) { //tempPath中结点为倒序
int id = tempPath[i];
tempW += weight[id];
tempHalfW += weight[id];
}
for(int i = tempPath.size() / 2; i < tempPath.size(); i++) {
int id = tempPath[i];
tempW += weight[id];
}
if(tempW % K > maxModW) {
maxModW = tempW % K;
minHalfW = tempHalfW;
path = tempPath;
} else if(tempW % K == maxModW && tempHalfW < minHalfW) {
minHalfW = tempHalfW;
path = tempPath;
}
tempPath.pop_back();
return;
}
tempPath.push_back(v);
for(int i = 0; i < pre[v].size(); i++) {
DFS(pre[v][i]);
}
tempPath.pop_back();
}
int main() {
init();
scanf("%d%d%d%d%d%d", &n, &m, &L, &K, &S, &T);
for(int i = 0; i < n; i++) {
scanf("%d", &weight[i]);
}
int u, v, w;
for(int i = 0; i < m; i++) {
scanf("%d%d%d", &u, &v, &w);
G[u][v] = G[v][u] = w;
}
BFS();
if(!inq[T]) {
printf("-1\n");
return 0;
}
Dijkstra(); //Dijkstra算法入口
DFS(T); //获取最优路径
printf("%d %d %d %d\n", numPath, d[T], maxModW, minHalfW);
for(int i = path.size() - 1; i >= 0; i--) {
printf("%d", path[i]); //倒着输出路径上的结点
if(i > 0) printf("->");
else printf("\n");
}
return 0;
}