1. 程式人生 > 其它 >WOS(Web of Science)檢索規則

WOS(Web of Science)檢索規則

P4042 [AHOI2014/JSOI2014]騎士遊戲

分析

這題的思路可太絕了。是對spfa的絕對深入理解

我們先簡單的分析題目。

從題目中我們可以知道,殺死一個魔物有兩種方法

  1. 普通攻擊,不能徹底解決魔物,還會分裂成其他魔物(可以是其他魔物且同一種魔物可以重複出現)
  2. 魔法攻擊,可以徹底解決魔物

來說說我的思路。

魔法攻擊

看到的時候,就想到,我們可以建立一個虛擬源點,從虛擬源點向所有點連一條邊,邊權就是將其徹底消滅的代價。

但實際上,我們並不需要真的建立出來,只需要在初始化代價dist陣列的時候,直接將魔殺值直接賦給對應的dist就可以了

但是怎麼知道用一個普通攻擊解決魔物,最後所需要付出的代價?

這裡我們著重說一下

普通攻擊

不難想到,i產生的新魔物,我們可以直接從i向產生的邊連一條邊。

這裡,我遇見了一個小問題,我們需要解決所有產生的魔物,才是普攻所得到的最優解,但是有時一個點產生了相同的魔物多個,怎麼辦?

其實這不是問題,我們用鏈式向前星的時候,只需要產生一個魔物就建一條邊,這樣對同一個魔物,我們就用邊數,知道了產生該魔物的數量。

那具體怎麼知道,我們解決這個魔物的普攻所需要付出的代價呢。可以直接累加。不清楚可以看程式碼

int res = ra[t];//ra[t]是t的普攻魔法
for(int i=h[t];~i;i=ne[i]) res += dist[e[i]];//dist[e[i]]就是解決e[i]需要付出的最小代價
dist[t] = max(res,dist[t]);

但我們的問題主要是

用普通攻擊解決魔物i後,我們需要確定瞭解決產生的新魔物的最小代價,才能得到對i而言到底怎樣才是最小代價

這裡,就是精彩的地方了

首先,我們來講講spfa

spfa之所以能用來處理Bellman-ford並進行優化,其原因是在Bellman-ford中,每一輪掃描的邊有很多都沒有可能更新其它點。spfa指出,當一條邊有希望更新它的一個端點的最短路值,當且僅當它的另一個端點的最短路值被更新過。spfa使用一個佇列保留這些可能被更新的資訊,通常,spfa在佇列中儲存的更新資訊的意義是"這個點被更新過,因而有可能通過與它連線的邊更新這些邊的其它端點"。直到佇列為空,此局面表示"沒有一個結點可能更新其它的結點

"。

這樣儲存更新資訊的原因是因為,在通常情況下,一個點可以通過與它相連的邊更新其它點,每一個結點的更新則是可能由多個通過邊連向它的結點決定,即一對多的更新。即因而我們儲存的資訊是"可能更新其他多個點的點"而不是"可能被其他點更新的點"因為這樣做更方便。(想一想,用前者則出隊一個結點掃描更新可以將所有由此結點被更新造成的可能被更新的結點的改變情況全部解決,而對於後者,一個結點的更新將所有連向它的結點入隊,則當這些結點出隊的時候,需要掃描它的所有前驅來決定結果,後者的複雜度顯然比前者高很多,相當於n^2和n的區別)。

在本題中,一個點(父魔頭)的取值(即殺死最優解)取決於多個點(子魔頭)的取值,也就是唯一一組確定的子魔頭(和其父魔頭的魔殺一起)決定了其父魔頭的擊殺取值,即多對一的關係。因而要保留更新資訊,用保留"有希望被它對應的子魔頭組更新的魔頭"(即第三段中說的第二種保留方式)的方式顯然更方便。

因此,我們再進行更新的時候,當一個節點被更新之後,我們需要將它的前驅節點全部放入佇列中,代表這些節點可以再次被更新了

所以我們將,上述程式碼再進行改變

auto t = q.front();
q.pop();
st[t] = 0;
LL res = ra[t];//ra[t]是t的普攻魔法
for(int i=h[t];~i;i=ne[i]) res += dist[e[i]];//dist[e[i]]就是解決e[i]需要付出的最小代價
if(res<dist[t]) 
{
    dist[t] = res;
    for(int i=hr[t];~i;i=ne[i])//hr[t]就是,記錄的t的前驅節點
    if(!st[e[i]])
    {
    q.push(e[i]);
    st[e[i]] = 1;
    }
}

以上就是,一個節點出佇列後會進行的操作。

溫馨提醒:記得開long long

附上全部程式碼

AC_code

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10,M = 2e6 + 10;
LL ra[N],dist[N];
int h[N],hr[N],e[M],ne[M],idx;
bool st[N];
int n;

void add(int h[],int a,int b)
{
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}

void spfa()
{
    queue<int> q;
    for(int i=1;i<=n;i++)
        q.push(i),st[i] = 1;
    while(q.size())
    {
        auto t = q.front();
        q.pop();
        st[t] = 0;
        LL res = ra[t];//ra[t]是t的普攻魔法
        for(int i=h[t];~i;i=ne[i]) res += dist[e[i]];//dist[e[i]]就是解決e[i]需要付出的最小代價
        if(res<dist[t]) 
        {
            dist[t] = res;
            for(int i=hr[t];~i;i=ne[i])//hr[t]就是,記錄的t的前驅節點
            if(!st[e[i]])
            {
            q.push(e[i]);
            st[e[i]] = 1;
            }
        }
    }
}

int main()
{
    cin>>n;
    memset(h,-1,sizeof h);
    memset(hr,-1,sizeof hr);
    for(int i=1;i<=n;i++)
    {
        int x;
        cin>>ra[i]>>dist[i]>>x;
        while(x--)
        {
            int a;cin>>a;
            add(h,i,a),add(hr,a,i);
        }
    }
    spfa();   
    cout<<dist[1]<<endl;
    return 0;
}