在遊戲中,JYY一共有兩種攻擊方式,一種是普通攻擊,一種是法術攻擊。兩種攻擊方式都會消耗JYY一些體力。採用普通攻擊進攻怪獸並不能把怪獸徹底殺死,怪獸的屍體可以變出其他一些新的怪獸,注意一個怪獸可能經過若干次普通攻擊後變回一個或更多同樣的怪獸;而採用法術攻擊則可以徹底將一個怪獸殺死。
遊戲世界中一共有種不同的怪獸,分別由到編號,現在號怪獸入侵村莊了,JYY想知道,最少花費多少體力值才能將所有村莊中的怪獸全部殺死呢?
我們從DP的思想來考慮這道題,設爲殺死怪獸的最小代價。
則我們可以列出一個很顯然的狀態轉移方程:
我們觀察到這是一個帶有環的DP式,我們可以用SPFA算法來解決帶有環的狀態轉移。
我們可以這樣實現:
- 將所有節點放入隊列並設置初始化,因爲排除題目干擾我們並不能找到一個固定的起點。
- 每一次將節點轉移完成以後,都將節點『殺死後產生的節點』入隊。
最後輸出即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5;
int read(void)
{
int s = 0, w = 0; char c = getchar();
while (c < '0' || c > '9') w |= c == '-', c = getchar();
while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
return w ? -s : s;
}
int n;
int dis[N], S[N], K[N], vis[N];
vector < int > a[N], b[N];
queue < int > q;
void SPFA(void)
{
for (int i=1;i<=n;++i) dis[i] = K[i];
//for (int i=1;i<=n;++i) vis[i] = 1, q.push(i);
vis[1] = 1, q.push(1);
while (q.size())
{
int x = q.front();
q.pop(), vis[x] = 0;
int sum = S[x];
for (int i=0;i<a[x].size();++i)
{
int y = a[x][i];
sum += dis[y];
}
if (sum >= dis[x]) continue;
dis[x] = sum;
for (int i=0;i<b[x].size();++i)
{
int y = b[x][i];
if (vis[y] == 0) {
q.push(y);
vis[y] = 1;
}
}
}
return;
}
signed main(void)
{
n = read();
for (int x=1;x<=n;++x)
{
S[x] = read();
K[x] = read();
int r = read();
while (r --)
{
int y = read();
a[x].push_back(y);
b[y].push_back(x);
}
}
SPFA();
cout << dis[1] << endl;
return 0;
}