题目描述:
基准时间限制:1 秒 空间限制:131072 KB 分值: 0
难度:基础题
你来到一个迷宫前。该迷宫由若干个房间组成,每个房间都有一个得分,第一次进入这个房间,你就可以得到这个分数。还有若干双向道路连结这些房间,你沿着这些道路从一个房间走到另外一个房间需要一些时间。游戏规定了你的起点和终点房间,你首要目标是从起点尽快到达终点,在满足首要目标的前提下,使得你的得分总和尽可能大。现在问题来了,给定房间、道路、分数、起点和终点等全部信息,你能计算在尽快离开迷宫的前提下,你的最大得分是多少么?
Input
第一行4个整数n (<=500), m, start, end。n表示房间的个数,房间编号从0到(n - 1),m表示道路数,任意两个房间之间最多只有一条道路,start和end表示起点和终点房间的编号。 第二行包含n个空格分隔的正整数(不超过600),表示进入每个房间你的得分。 再接下来m行,每行3个空格分隔的整数x, y, z (0<z<=200)表示道路,表示从房间x到房间y(双向)的道路,注意,最多只有一条道路连结两个房间, 你需要的时间为z。 输入保证从start到end至少有一条路径。
Output
一行,两个空格分隔的整数,第一个表示你最少需要的时间,第二个表示你在最少时间前提下可以获得的最大得分。
Input示例
3 2 0 2 1 2 3 0 1 10 1 2 11
Output示例
21 6
解题思路:
太久没有写图论的题了,看到这道题居然没有反应过来用最短路径算法来求,先用深搜来做,结果超时了,可以先看下深搜代码:
#include <cstdio>
#include <vector>
using namespace std;
int n, m, start, end;
int minTime = 900000, maxScore = -1;
struct info{
int to, cost;
info(int t, int c):to(t), cost(c){}
};
int score[509];
vector<info> road[509];
int visit[509] = {0};
void dfs(int start, int cost, int sco){
if(cost > minTime || (cost == minTime && sco < maxScore))
return;
if(start == end){
if(cost < minTime)
minTime = cost, maxScore = sco;
else if(cost == minTime && sco > maxScore)
maxScore = sco;
return;
}
visit[start] = 1;
for(int i = 0; i < road[start].size(); i++){
int to = road[start][i].to;
if(!visit[to]){
dfs(to, cost + road[start][i].cost, sco + score[to]);
}
}
visit[start] = 0;
}
int main(){
scanf("%d%d%d%d", &n, &m, &start, &end);
for(int i = 0; i < n; i++)
scanf("%d", &score[i]);
int x, y, z;
for(int i = 0; i < m; i++){
scanf("%d%d%d", &x, &y, &z);
road[x].push_back(info(y, z));
road[y].push_back(info(x, z));
}
dfs(start, 0, score[start]);
printf("%d %d\n", minTime, maxScore);
return 0;
}
下面用dijskstra算法来做,下面的做法是通过优先队列,也就是binary heap来做的,所以时间复杂度是:O(|V+E| * log(|V|) ),思路基本和dijskstra算法一致,就是路径长度不能单纯利用所用时间来表示,还需要用得分来表示,这里用结构体info来表示,里面之所以会有position,是因为在priority_queue中,得到最短路后,还需要知道当前的位置。代码中已经有详细的注释了(不知为什么突然在我的vim中敲不了中文,就用英文来写注释了。。。)
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
int n, m, start, end;
struct node{
int to, cost;
node(int t, int c):to(t), cost(c){}
};
int scores[509];
vector<node> road[509];
struct info{
int position, cost, score;
info(int p, int c, int s):position(p), cost(c), score(s){}
info(){}
//note: in priority_queue top return the greater default;
//also here we can reverse the comparision;
//it is easy to understant that the right side of the operator < is better.
bool operator < (const info &t) const{
if(cost == t.cost){
return score < t.score;
}
else{
return cost < t.cost;
}
}
};
info dis[509];
void dijkstra(){
for(int i = 0; i < n; i++){
dis[i].cost = -900000;
dis[i].score = 0;
dis[i].position = i;
}
dis[start].cost = 0;
dis[start].score = scores[start];
priority_queue<info> q;
q.push(dis[start]);
while(!q.empty()){
info current = q.top();
q.pop();
int curPosition = current.position;
//if there is the other ocurrences in the queue,
//the current.cost will bigger than dis[to]
if(dis[curPosition].cost == current.cost && dis[curPosition].score == current.score){
for(int i = 0; i < road[curPosition].size(); i++){
int to = road[curPosition][i].to;
info newDis(to, dis[curPosition].cost + road[curPosition][i].cost,
dis[curPosition].score + scores[to]);
//note: we have overWrite the operator < method
if(dis[to] < newDis){
dis[to] = newDis;
q.push(dis[to]);
}
}
}
}
}
int main(){
scanf("%d%d%d%d", &n, &m, &start, &end);
for(int i = 0; i < n; i++)
scanf("%d", &scores[i]);
int x, y, z;
for(int i = 0; i < m; i++){
scanf("%d%d%d", &x, &y, &z);
//note: we save the negative distance
road[x].push_back(node(y, -z));
road[y].push_back(node(x, -z));
}
dijkstra();
printf("%d %d\n", -dis[end].cost, dis[end].score);
return 0;
}
另附上没有用优先队列优化的dijskstra算法,代码看起来就比较简洁:
#include <cstdio>
#include <vector>
using namespace std;
int n, m, start, end;
int scores[509];
vector<int> path[509];
vector<int> time[509];
int minTime[509] = {0};
int maxScore[509] = {0};
void dijkstra(){
bool visit[509] = {0};
int cur = start;
visit[cur] = 1;
while(cur != end){
for(int i = 0; i < path[cur].size(); i++){
int to = path[cur][i];
int cost = minTime[cur] + time[cur][i];
int score = maxScore[cur] + scores[to];
if(minTime[to] == 0 || cost < minTime[to] ||
(cost == minTime[to] && score > maxScore[to]) ){
minTime[to] = cost;
maxScore[to] = score;
}
}
int min = 9000000;
int max = 0;
for(int i = 0; i < n; i++){
if(!visit[i] && minTime[i] != 0 &&
(minTime[i] < min || (minTime[i] == min && maxScore[i] < max) ) ){
min = minTime[i];
max = maxScore[i];
cur = i;
}
}
visit[cur] = 1;
}
}
int main(){
scanf("%d%d%d%d", &n, &m, &start, &end);
for(int i = 0; i < n; i++){
scanf("%d", &scores[i]);
maxScore[i] = scores[i];
}
int x, y, z;
for(int i = 0; i < m; i++){
scanf("%d%d%d", &x, &y, &z);
path[x].push_back(y);
path[y].push_back(x);
time[x].push_back(z);
time[y].push_back(z);
}
dijkstra();
printf("%d %d\n", minTime[end], maxScore[end]);
return 0;
}