Problem 3373 騎士遊戲
長期的宅男生活中,JYY 又挖掘出了一款 RPG 遊戲。在這個遊戲中 JYY 會扮演一個英勇的騎士,用他手中的長劍去殺死入侵村莊的怪獸。
在這個遊戲中,JYY 一共有兩種攻擊方式,一種是普通攻擊,一種是法術攻擊。兩種攻擊方式都會消耗 JYY 一些體力。採用普通攻擊進攻怪獸並不能把怪獸徹底殺死,怪獸的屍體可以變出其他一些新的怪獸,注意一個怪獸可能經過若干次普通攻擊後變回一個或更多同樣的怪獸;而採用法術攻擊則可以徹底將一個怪獸殺死。當然了,一般來說,相比普通攻擊,法術攻擊會消耗更多的體力值(但由於遊戲系統 bug,並不保證這一點)。
遊戲世界中一共有N種不同的怪獸,分別由 1到 N編號,現在 1號怪獸入侵村莊了,JYY 想知道,最少花費多少體力值才能將所有村莊中的怪獸全部殺死呢?
分析:
1. 顯然殺死一種怪物有兩種方法,直接消滅和殺死它和產生的所有怪物。
我們需要比較 Eliminate[i] 和 Kill[i] +Sum{ costs that need to eliminate following monsters}
2. 怪物們之間時有類似於“路徑”的概念,殺死a,掉出b,c。自然a與b,a與c之間會有一條連接相連。
3. 現在思考在於怎麼分別討論“消滅”和“殺死”的情況。
對於單個元素a 只需要簡單討論eliminate[a] 以及kill[a]的大小
對於簡單路徑a->b 只需要討論 eliminate[a], kill[a] + min(eliminate[b],kill[n]) 的大小
4. 但問題在於,當我們在使用“最短路徑”來解決題目的時候,一般都是已知起點,終點。然後再去跑算法,尋找這個值的大小。這道問題卻是你只知道起點,路上的任何一個地方都有可能是終點,也有可能不是終點。
5. 其次,我們求的也不是特定一段距離,特定的,我們只想瞭解到底是消滅1號怪物的開銷大,還是殺死並消滅剩餘敵人的開銷大。
6. 從某種意義上而言,我們需要遞歸尋找。單可以看出其中的記憶性,動態規劃的思想。如果我們從這個圖最外面開始看起,一層一層往裏計算消滅怪物的開銷,自然而然題目就解決了
7. 但是,與普通簡單DP不同的是,可能有環的存在
但事實上似乎並不用擔心。不妨令每個點的初值都等於eliminate[i],在最短路徑計算過程中:如SPFA,邊的鬆弛都是依靠距離的。自然,正環的產生必定會大於最短距離,有得保證。
8. 憑什麼保證你從這個點開始計算一定是最優的?不能保證!所以不如干脆就把所有點一開始就放在隊列裏。
那麼怎麼保證隊列的順序一定能出最優解?不能保證!所以不如干脆每次一旦更新成功,就把父節點再次入隊列(只可能有一個父節點)。這樣一來就無後顧之憂了。
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
#define N 201000
#define M 2010000
#define inf 0x3f3f3f3f
using namespace std;
typedef long long long_int;
long_int A[N];
long_int Dist[N];
bool inq[N];
int n = 0;
vector<int> G[N],P[N];
void Add(int u ,int v){
G[u].push_back(v);
P[v].push_back(u);
}
void SPFA(){
queue<int> Q;
for (int i = 1 ; i <= n ; ++i)
{
Q.push(i);
inq[i] = true;
}
while(!Q.empty())
{
int u = Q.front();Q.pop();inq[u] = false;
long long temp = A[u];
for (int i = 0 ; i < G[u].size() ; ++i)
temp += Dist[G[u][i]];
if(temp >= Dist[u]) continue;
Dist[u] = temp;
for (int i = 0 ; i < P[u].size() ; ++i)
{
if(!inq[P[u][i]])
{
inq[P[u][i]] = true;
Q.push(P[u][i]);
}
}
}
}
int main(){
cin >> n;
int x = 0;
for (int i = 1 ; i <= n ; ++i)
{
int xx = 0;
cin >> A[i] >> Dist[i] >> x;
while(x--)
{
cin >> xx;
Add(i,xx);
}
}
SPFA();
cout << Dist[1] << endl;
return 0;
}