[Luogu P4042][AHOI2014/JSOI2014]骑士游戏
这道题上手一看,直接对怪兽建图,然后dfs搞个dp就行,最后很显然的,图太大dfs栈爆了。
好的考虑正解,其实每个怪兽的体力消耗,就是魔法杀死这个怪兽的消耗和普攻杀死它及生出的所有怪兽的消耗取最小值。那目测一下,求最小值,就和最短路有联系了。但是在跑最短路过程中会出现某个点dis值改变(也就是更新了杀死这个怪兽的方式)从而导致所有dis值全部改变,直接上我最爱的dijkstra就不好用了。这个时候就得想想spfa。
其实这道题考的是算法原理,dijkstra其实是维护两个集合,已知最短路的集合向未知集合扩展,本质是贪心。而SPFA,也就是队列优化的Bellman-Ford算法,是一个高级点的暴力,对每一个点都尝试进行松弛操作。那么这个原理就很适合这道题了,如果一个点dis值变了,不需要重跑最短路,只需要枚举这个点所有的前驱,重新进行松弛操作就行,即使是最坏情况O(n²)开了O2也勉强水的过去233333
做题用时大概1小时。
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> #define ll long long #define N 200005 #define int long long using namespace std; vector<ll>g[N]; vector<ll>from[N]; queue<ll>q; bool vis[N]; ll dis[N],a[N]; ll n; void spfa() { while(!q.empty()) { ll u = q.front(); q.pop(); vis[u] = 0; ll tmp = a[u]; for(ll i = 0;i < g[u].size();i++) { ll v = g[u][i]; tmp += dis[v]; } if(tmp > dis[u]) continue; dis[u] = tmp; for(ll i = 0;i < from[u].size();i++) { if(!vis[from[u][i]]) { q.push(from[u][i]); vis[from[u][i]] = 1; } } } } void add(ll u,ll v) { g[u].push_back(v); from[v].push_back(u); return; } main() { scanf("%lld",&n); for(ll i = 1;i <= n;i++) { q.push(i); vis[i] = 1; ll t; scanf("%lld %lld %lld",&a[i],&dis[i],&t); for(ll j = 1;j <= t;j++) { ll v; scanf("%lld",&v); add(i,v); } } spfa(); printf("%lld",dis[1]); }