bzoj 3875 骑士游戏 - spfa - 动态规划
Description
【故事背景】
长期的宅男生活中,JYY又挖掘出了一款RPG游戏。在这个游戏中JYY会
扮演一个英勇的骑士,用他手中的长剑去杀死入侵村庄的怪兽。
【问题描述】
在这个游戏中,JYY一共有两种攻击方式,一种是普通攻击,一种是法术攻
击。两种攻击方式都会消耗JYY一些体力。采用普通攻击进攻怪兽并不能把怪兽彻底杀死,怪兽的尸体可以变出其他一些新的怪兽,注意一个怪兽可能经过若干次普通攻击后变回一个或更多同样的怪兽;而采用法术攻击则可以彻底将一个怪兽杀死。当然了,一般来说,相比普通攻击,法术攻击会消耗更多的体力值(但由于游戏系统bug,并不保证这一点)。
游戏世界中一共有N种不同的怪兽,分别由1到N编号,现在1号怪兽入
侵村庄了,JYY想知道,最少花费多少体力值才能将所有村庄中的怪兽全部杀死呢?
Input
第一行包含一个整数N。
接下来N行,每行描述一个怪兽的信息;
其中第i行包含若干个整数,前三个整数为Si,Ki和Ri,表示对于i号怪兽,
普通攻击需要消耗Si的体力,法术攻击需要消耗Ki的体力,同时i号怪兽死亡后会产生Ri个新的怪兽。表示一个新出现的怪兽编号。同一编号的怪兽可以出现多个。
Output
输出一行一个整数,表示最少需要的体力值。
Sample Input
4
4 27 3 2 3 2
3 5 1 2
1 13 2 4 2
5 6 1 2
4 27 3 2 3 2
3 5 1 2
1 13 2 4 2
5 6 1 2
Sample Output
26
HINT
【样例说明】
首先用消耗4点体力用普通攻击,然后出现的怪兽编号是2,2和3。花费
10点体力用法术攻击杀死两个编号为2的怪兽。剩下3号怪兽花费1点体力进
行普通攻击。此时村庄里的怪兽编号是2和4。最后花费11点体力用法术攻击
将这两只怪兽彻底杀死。一共花费的体力是4+5+5+1+5+6=26。
【数据范围】
2<=N<=2*10^5,1<=Ri,Sigma(Ri)<=10^6,1<=Ki,Si<=5*10^14
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 }