1. 程式人生 > >luoguP1339 [USACO09OCT]熱浪Heat Wave(spfa入門

luoguP1339 [USACO09OCT]熱浪Heat Wave(spfa入門

我記得我當時剛接觸信奧的時候,旁邊的學長頁面上顯示的是a了這道題,當時的想著,什麼時候我能把這道題寫出來我就厲害了,然而今天發現自己,,,不說了,看題。
前幾天,我在卡了很長時間圖論後,終於學了克魯斯卡爾,算是打破了我原來圖論的空白,而今天我看著dalao的程式碼學了一遍了spfa,(迪傑斯特拉往後放放吧,嘗試多次無功而返的痛啊~~~),
先看一個板子:
首先呢這是luoguP3371 【模板】單源最短路徑(弱化版)(不卡spfa
這是spfa的模板

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
struct edge {
    ll from;//與這條表最近的父親節點一樣的邊的編號 
    ll to;//此邊的子節點 
    ll w;//此邊的權值 
};
inline ll read(){//讀入優化
    int x = 0;int f = 1;char c = getchar();
    while(c<'0'||c>'9'){
        if(c=='-')f=-f;
        c=getchar();
    } 
    while(c<='9'&&c>='0'){
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
const ll maxn = 500521;
const ll INF = 2147483647; 
struct edge e[maxn];
int n;
int m; 
int s;
int p;
int head[maxn];
int dis[maxn];//s點到某點的距離
int vis[maxn];//該點是否進隊0未進,1已進
queue<int>que;
void add(int a,int b,int c){//鄰接表
    p++;
    e[p].to = b;
    e[p].w  = c;
    e[p].from = head[a];
    head[a] = p;//以某點為父親節點引出的最後一條邊 
}
void spfa(){
    for(int i=1;i<=n;i++){
        dis[i] = INF ;//初始化 
        vis[i] = 0;//記錄一個點是否在佇列中 
    }
    que.push(s);//第一個頂點入隊 
     dis[s] = 0;//初始化 
     vis[s] = 1; //s點已經入隊
     while(!que.empty()){//佇列如果不是空的 
        //佇列空時返回true 
        int u = que.front();//取出隊首
          que.pop();//彈出該點 
         vis[u] = 0;//該點出隊
          for(int i=head[u];i;i=e[i].from){//遍歷鄰接表
            int v = e[i].to;
            if(dis[v]>dis[u]+e[i].w){
        //如果s到u點再到v點的距離小於當前已知的最短距離 
                dis[v] = dis[u]+e[i].w;//更新資料 
                if(vis[v]==0)//未入隊 
            {
                vis[v] = 1;//入隊 
                  que.push(v); 
               } 
              }
          } 
     } 
               return;//想著寫上算是一個好習慣吧
}
int main(){
    n = read();m = read();s = read();
    for(int i=1;i<=m;i++){
        int u,v,j;
        u = read();
        v = read();
        j = read();
        add(u,v,j); 
    } 
    spfa();
    for(int i=1;i<=n;i++){
        if(s==i)printf("0 ");
        else printf("%d ",dis[i]);
    }
    
//system("pause"); 
    return 0;
} 

spfa要點步驟:
1.鄰接表存邊;
2.初始化,dis陣列與vis陣列;
3.從起點開始找,所以起點先入隊;
4迴圈遍歷鄰接表,將符合條件的各點依次推入佇列;
5.在迴圈遍歷的同時更新每個點的資料,併入隊;

好了看了spfa的板子再來看這道題就簡單多了;

題目描述

The good folks in Texas are having a heatwave this summer. Their Texas Longhorn cows make for good eating but are not so adept at creating creamy delicious dairy products. Farmer John is leading the charge to deliver plenty of ice cold nutritious milk to Texas so the Texans will not suffer the heat too much.

FJ has studied the routes that can be used to move milk from Wisconsin to Texas. These routes have a total of T (1 <= T <= 2,500) towns conveniently numbered 1..T along the way (including the starting and ending towns). Each town (except the source and destination towns) is connected to at least two other towns by bidirectional roads that have some cost of traversal (owing to gasoline consumption, tolls, etc.). Consider this map of seven towns; town 5 is the

source of the milk and town 4 is its destination (bracketed integers represent costs to traverse the route):


                              [1]----1---[3]-
                             /               \
                      [3]---6---[4]---3--[3]--4
                     /               /       /|
                    5         --[3]--  --[2]- |
                     \       /        /       |
                      [5]---7---[2]--2---[3]---
                            |       /
                           [1]------

Traversing 5-6-3-4 requires spending 3 (5->6) + 4 (6->3) + 3 (3->4) = 10 total expenses.

Given a map of all the C (1 <= C <= 6,200) connections (described as two endpoints R1i and R2i (1 <= R1i <= T; 1 <= R2i <= T) and costs (1 <= Ci <= 1,000), find the smallest total expense to traverse from the starting town Ts (1 <= Ts <= T) to the destination town Te (1 <= Te <= T).

德克薩斯純樸的民眾們這個夏天正在遭受巨大的熱浪!!!他們的德克薩斯長角牛吃起來不錯,可是他們並不是很擅長生產富含奶油的乳製品。Farmer John此時以先天下之憂而憂,後天下之樂而樂的精神,身先士卒地承擔起向德克薩斯運送大量的營養冰涼的牛奶的重任,以減輕德克薩斯人忍受酷暑的痛苦。

FJ已經研究過可以把牛奶從威斯康星運送到德克薩斯州的路線。這些路線包括起始點和終點先一共經過T (1 <= T <= 2,500)個城鎮,方便地標號為1到T。除了起點和終點外地每個城鎮由兩條雙向道路連向至少兩個其它地城鎮。每條道路有一個通過費用(包括油費,過路費等等)。

給定一個地圖,包含C (1 <= C <= 6,200)條直接連線2個城鎮的道路。每條道路由道路的起點Rs,終點Re (1 <= Rs <= T; 1 <= Re <= T),和花費(1 <= Ci <= 1,000)組成。求從起始的城鎮Ts (1 <= Ts <= T)到終點的城鎮Te(1 <= Te <= T)最小的總費用。

輸入輸出格式

輸入格式:

第一行: 4個由空格隔開的整數: T, C, Ts, Te

第2到第C+1行: 第i+1行描述第i條道路。有3個由空格隔開的整數: Rs, Re和Ci

輸出格式:

一個單獨的整數表示從Ts到Te的最小總費用。資料保證至少存在一條道路。

輸入輸出樣例

輸入樣例#1: 複製
7 11 5 4
2 4 2
1 4 3
7 2 2
3 4 3
5 7 5
7 3 3
6 1 1
6 3 4
2 4 3
5 6 3
7 2 1

輸出樣例#1: 複製

7

說明

【樣例說明】

5->6->1->4 (3 + 1 + 3)

首先呢,這是一個無向圖,由此可以知道,我們存邊的時候需要存兩次,正著存一次,反著存一次;其次呢,好吧,其次就是輸出是輸出dis[te],在我的程式碼裡是dis[h];

我呢就是模板複製貼上修改一下,就過了,附AC程式碼;

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
struct edge {
    ll from;//與這條表最近的父親節點一樣的邊的編號 
    ll to;//此邊的子節點 
    ll w;//此變得權值 
};
inline ll read(){
    int x = 0;int f = 1;char c = getchar();
    while(c<'0'||c>'9'){
        if(c=='-')f=-f;
        c=getchar();
    } 
    while(c<='9'&&c>='0'){
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
const ll maxn = 500521;
const ll INF = 2147483647; 
struct edge e[maxn];
int n;
int h;
int m; 
int s;
int p;
int head[maxn];
int dis[maxn];
int vis[maxn];
queue<int>que;
void add(int a,int b,int c){
    p++;
    e[p].to = b;
    e[p].w  = c;
    e[p].from = head[a];
    head[a] = p;//以某點為父親節點引出的最後一條邊 
}
void spfa(){
    for(int i=1;i<=n;i++){
        dis[i] = INF ;//初始化 
        vis[i] = 0;//記錄一個點是否在佇列中 
    }
    que.push(s);//第一個頂點入隊 
     dis[s] = 0;//初始化 
     vis[s] = 1;
      //s點已經入隊
     while(!que.empty()){//佇列如果不是空的 
        //佇列空時返回true 
        int u = que.front();//取出隊首
          que.pop();//彈出該點 
         vis[u] = 0;//該點出隊
          for(int i=head[u];i;i=e[i].from){
            int v = e[i].to;
            if(dis[v]>dis[u]+e[i].w){
        //如果s到u點再到v點的距離小於當前已知的最短距離 
                dis[v] = dis[u]+e[i].w;//更新資料 
                if(vis[v]==0)//未入隊 
            {
                vis[v] = 1;//入隊 
                  que.push(v); 
               } 
              }
          } 
     } 
}
int main(){
    n = read();m = read();s = read();h=read();
    for(int i=1;i<=m;i++){
        int u,v,j;
        u = read();
        v = read();
        j = read();
        add(u,v,j); 
        add(v,u,j); 
    } 
    spfa();
     printf("%d ",dis[h]);
    
    
//system("pause"); 
    return 0;
}