51nod 1459 迷宮遊戲

題目描述:

基準時間限制: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;
}


仔細分析一下複雜度,雖說有對不符合條件的部分進行篩選,但在最差的情況下仍是O(2^n)的時間複雜度,因爲每個點都可能被反覆搜索一遍,超時也理所當然了。


下面用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;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章