description
在这个游戏中,JYY一共有两种攻击方式,一种是普通攻击,一种是法术攻击。两种攻击方式都会消耗JYY一些体力。采用普通攻击进攻怪兽并不能把怪兽彻底杀死,怪兽的尸体可以变出其他一些新的怪兽,注意一个怪兽可能经过若干次普通攻击后变回一个或更多同样的怪兽;而采用法术攻击则可以彻底将一个怪兽杀死。当然了,一般来说,相比普通攻击,法术攻击会消耗更多的体力值(但由于游戏系统bug,并不保证这一点)。
游戏世界中一共有N种不同的怪兽,分别由1到N编号,现在1号怪兽入侵村庄了,JYY想知道,最少花费多少体力值才能将所有村庄中的怪兽全部杀死呢?
analysis
-
可以设f[i]f[i]f[i]表示彻底杀死iii怪兽的最小值,则f[i]=min(k[i],s[i]+∑j∈sonf[j])f[i]=min(k[i],s[i]+\sum_{j\in son}f[j])f[i]=min(k[i],s[i]+∑j∈sonf[j])
-
但是这种转移可以成环,所以不能用DPDPDP做,而这种类似松弛操作可以用SPFASPFASPFA来做
-
由于初始不知道从哪个点开始更新会最优,初始所有点都入队
-
dis[i]dis[i]dis[i]表示最小花费,一开始也不知道物理攻击以后如何转化,先都赋值成k[i]k[i]k[i]
-
更新nownownow即为枚举所有nownownow的儿子,拿∑j∈sondis[j]\sum_{j\in son}dis[j]∑j∈sondis[j]来更新dis[now]dis[now]dis[now]
-
对于队首nownownow的更新,它的变动可能影响到所有经平A后可以得到nownownow的点
-
所以建反向边,若队首更新成功,把可以得到队首的点全部再入队
-
dis[1]dis[1]dis[1]即为答案。这个做法时间复杂度应该是很大的……
code
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#define MAXN 200005
#define MAXM 1000005
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)
#define rep(i,a) for (reg i=las[a];i;i=nex[i])
#define rep1(i,a) for (reg i=las1[a];i;i=nex1[i])
using namespace std;
ll las[MAXM],nex[MAXM],tov[MAXM];
ll las1[MAXM],nex1[MAXM],tov1[MAXM];
ll dis[MAXN],phy[MAXN];
ll n,tot,tot1;
bool bz[MAXN];
queue<ll>q;
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline ll max(ll x,ll y){return x>y?x:y;}
inline ll min(ll x,ll y){return x<y?x:y;}
inline void link(ll x,ll y){nex[++tot]=las[x],las[x]=tot,tov[tot]=y;}
inline void link1(ll x,ll y){nex1[++tot1]=las1[x],las1[x]=tot1,tov1[tot1]=y;}
int main()
{
freopen("knight.in","r",stdin);
freopen("knight.out","w",stdout);
n=read();
fo(i,1,n)
{
phy[i]=read(),dis[i]=read();ll tmp=read(),x;
while (tmp--)x=read(),link(i,x),link1(x,i);
}
fo(i,1,n)q.push(i);
while (!q.empty())
{
ll now=q.front(),tmp=0;
q.pop(),bz[now]=1;
rep(i,now)tmp+=dis[tov[i]];
if (phy[now]+tmp<dis[now])
{
dis[now]=phy[now]+tmp;
rep1(i,now)if (bz[tov1[i]])q.push(tov1[i]),bz[tov1[i]]=0;
}
}
printf("%lld\n",dis[1]);
return 0;
}

JYY在一款游戏中面临两种攻击方式:普通攻击和法术攻击。普通攻击无法彻底杀死怪兽,只会使其变出更多同类;法术攻击则能一击必杀,但可能消耗更多体力。游戏包含N种不同怪兽,JYY需要找出消灭所有怪兽的最低体力消耗。通过分析,发现这是一个存在环的最短路径问题,不适合直接用DP解决,而是采用SPFA算法。初始所有点入队,不断更新节点的最小花费,最后dis[1]即为答案。虽然这种方法的时间复杂度较高。
384

被折叠的 条评论
为什么被折叠?



