題目描述:
第一行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至少有一條路徑。
一行,兩個空格分隔的整數,第一個表示你最少需要的時間,第二個表示你在最少時間前提下可以獲得的最大得分。
3 2 0 2 1 2 3 0 1 10 1 2 11
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;
}