题目描述
在这个游戏中,JYY一共有两种攻击方式,一种是普通攻击,一种是法术攻击。两种攻击方式都会消耗JYY一
些体力。采用普通攻击进攻怪兽并不能把怪兽彻底杀死,怪兽的尸体可以变出其他一些新的怪兽,注意一个
怪兽可能经过若干次普通攻击后变回一个或更多同样的怪兽;而采用法术攻击则可以彻底将一个怪兽杀死。
当然了,一般来说,相比普通攻击,法术攻击会消耗更多的体力值(但由于游戏系统bug,并不保证这一点)。
游戏世界中一共有 \(N\) 种不同的怪兽,分别由1到 \(N\) 编号,现在1号怪兽入侵村庄了,JYY想知道,
最少花费多少体力值才能将所有村庄中的怪兽全部杀死呢?
说明/提示
首先用消耗4点体力用普通攻击,然后出现的怪兽编号是2,2和3。花费10点体力用法术攻击杀死两个
编号为2的怪兽。剩下3号怪兽花费1点体力进行普通攻击。此时村庄里的怪兽编号是2和4。
最后花费11点体力用法术攻击将这两只怪兽彻底杀死。一共花费的体力是4+5+5+1+5+6=26。
对于所有数据 \(2 \le N \le 2 \times 10^5,1 \le R_i,\sum R_i \le 10^6,1 \le K_i,S_i \le 5 \times 10^{14}\)
这道题,我们很容易就想到转移方程
设 \(f_i\) 表示消灭第 \(i\) 只怪兽的最小代价
那么转移方程就可以写成 \(f_i = min(k_i,s_i+\displaystyle\sum f[R[i][j])\)
可是这会出现环也就是有后效性的dp,我们就需要一种方法来处理这个。
当后面的 \(k_i\) 变小时,那么前面的 \(f_i\)也会发生变化。
这不就类似于spfa的松弛操作吗?
于是,我们就可以每次求完一个点的\(f\)值后,如果他的\(f_i\)变小,那我们需要将能变成它的怪兽在重新入队
在进行更新,直到所有的点都不能更新为止。
但一般的最短路都是有起点和终点的,这个题我们无法确定从那个点开始更优。
所以,我们干脆一开始把所有点都加入队列中,都进行更新。
以上就是我们spfa处理有后效性dp的思路
Code
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
using namespace std;
#define int long long//不开long long 见祖宗
const int N = 2e5+10;
int n,tot,cnt,num,x;
int s[N],dis[N],head[N],hed[N];
bool vis[N];
inline int read()
{
int s = 0, w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
return s * w;
}
queue<int> q;
struct node{int to,net;} e[1000010],e2[1000010];
void add(int x,int y)
{
e[++tot].to = y;
e[tot].net = head[x];
head[x] = tot;
}
void Add(int x,int y)
{
e2[++cnt].to = y;
e2[cnt].net = hed[x];
hed[x] = cnt;
}
signed main()
{
n = read();
for(int i = 1; i <= n; i++)
{
s[i] = read(); dis[i] = read(); num = read();//一开始杀死一只怪兽的代价为魔法攻击消耗的体力值
for(int j = 1; j <= num; j++)
{
x = read(); add(i,x); Add(x,i);
}
}
for(int i = 1; i <= n; i++) q.push(i), vis[i] = 1;//一开始把所有的点都进入队列中
while(!q.empty())
{
int t = q.front(); q.pop(); vis[t] = 0; int tmp = s[t];
for(int i = head[t]; i; i = e[i].net)//计算把他变成其他怪兽,并将它们消灭的最小代价
{
int to = e[i].to;
tmp += dis[to];
}
if(tmp <= dis[t])//如果杀死他的代价变小了,我们就要把能变成他的怪兽都重新入队在进行次更新
{
dis[t] = tmp;
for(int i = hed[t]; i; i = e2[i].net)
{
int to = e2[i].to;
if(!vis[to])
{
q.push(to);
vis[to] = 1;
}
}
}
}
// for(int i = 1; i <= n; i++) cout<<dis[i]<<endl;
printf("%lld\n",dis[1]);
return 0;
}