Description
Input
Output
输出一行一个整数,表示最少需要的体力值。
Sample Input
4 27 3 2 3 2
3 5 1 2
1 13 2 4 2
5 6 1 2
Sample Output
HINT
Source
题目大意
一共有$n$种怪兽,第$i$种怪兽遭到普通攻击后会分裂成若干个小怪兽,会被法术攻击彻底消灭。对于每种怪兽使用法术攻击和普通攻击消耗的体力值是不同的。
现在,1只种类为1的怪兽入侵村庄,问彻底消灭村庄内的怪兽至少需要的体力值。
依稀记得以前有过spfa套dp的脑洞,但没想到真有人出这么一道题
显然可以看见动态规划的影子,以及显而易见的方程:
$f[i] = \max \left(k_{i}, s_{i} + \sum_{j}f[j] \right ) $
真棒,转移有环。那么可以解方程吗?至少我不会,这里可要做决策。
那看看最短路的动态规划的方程
$f[i] = \min\left \{ f[j] + w\left(j, i \right ) \right \}$
显然它也是有环的。但是spfa通过反复进行更新来得到最优解,即当一个状态被更新后,就把它放入队列去更新它的后继状态。
这道题也可以采用同样的做法。
Code
1 /** 2 * bzoj 3 * Problem#3875 4 * Accepted 5 * Time: 5496ms 6 * Memory: 22764k 7 */ 8 #include <bits/stdc++.h> 9 #ifndef WIN32 10 #define Auto "%lld" 11 #else 12 #define Auto "%I64d" 13 #endif 14 using namespace std; 15 typedef bool boolean; 16 #define ll long long 17 18 int n; 19 ll *ss; 20 vector<int> *suf; 21 vector<int> *pre; 22 ll* f; 23 queue<int> que; 24 25 inline void init() { 26 scanf("%d", &n); 27 f = new ll[(n + 1)]; 28 ss = new ll[(n + 1)]; 29 suf = new vector<int>[(n + 1)]; 30 pre = new vector<int>[(n + 1)]; 31 for (int i = 1, u, x; i <= n; i++) { 32 scanf(Auto""Auto"%d", ss + i, f + i, &u); 33 while (u--) { 34 scanf("%d", &x); 35 suf[i].push_back(x); 36 pre[x].push_back(i); 37 } 38 } 39 } 40 41 boolean *vis; 42 void spfa() { 43 vis = new boolean[(n + 1)]; 44 memset(vis, true, sizeof(boolean) * (n + 1)); 45 for (int i = 1; i <= n; i++) 46 que.push(i); 47 while (!que.empty()) { 48 int e = que.front(); 49 ll cmp = ss[e]; 50 que.pop(); 51 vis[e] = false; 52 for (int i = 0; i < (signed) suf[e].size(); i++) 53 cmp += f[suf[e][i]]; 54 if (cmp < f[e]) { 55 f[e] = cmp; 56 for (int i = 0; i < (signed) pre[e].size(); i++) { 57 int eu = pre[e][i]; 58 if (!vis[eu]) { 59 que.push(eu); 60 vis[eu] = true; 61 } 62 } 63 } 64 } 65 } 66 67 inline void solve() { 68 spfa(); 69 printf(Auto"\n", f[1]); 70 } 71 72 int main() { 73 init(); 74 solve(); 75 return 0; 76 }