Live2D

【AHOI2014/JSOI2014】骑士游戏

JYY一共有两种攻击方式,一种是普通攻击,一种是法术攻击

两种攻击方式都会消耗JYY一些体力。采用普通攻击进攻怪兽并不能把怪兽彻底杀死
怪兽的尸体可以变出其他一些新的怪兽,注意一个怪兽可能经过若干次普通攻击后变回一个或更多同样的怪兽
而采用法术攻击则可以彻底将一个怪兽杀死

游戏世界中一共有N种不同的怪兽,分别由1到N编号
在1号怪兽入侵村庄了,JYY想知道,最少花费多少体力值才能将所有村庄中的怪兽全部杀死呢

这真的是一道好题啊……
一开始想的是,使用普通攻击杀死一个怪物,产生了分裂体\(a_1\)~\(a_x\)
那么\(ans[i]=min(f[i]+ans[a1]+...+ans[an],g[i])\)
乱写的式子,就是说杀死其本体分裂体的最小价值与直接杀死本体的价值取min

但是这样一直递归下去求解的话……好像没有初始位置来着?
分裂关系可能成环,那dp就废了

打开题解看一眼仔细思索后发现居然要用spfa建模?

类比于跳楼机那道题,这个dp方程像spfa的松弛操作

那么类似的,我们搞个最短路来玩?

不过发现,并不能保证从一个定点开始跑出来的答案是最优的,所以初始化的时候要把所有节点入队

但现在仍然不能保证这样可以搞出正确答案啊?
毕竟这个队列的顺序并没有正确性
所以一旦更新了答案,就把当前点的父节点再次入队,看它的儿子是否有更优的解

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 600005
using namespace std;

int n,u,v,k,x;
int a[N];
ll dis[N];

struct Edge
{
	int next,to;
}edge[N<<1],edge2[N<<1];
int cnt=0,head[N],cnt2=0,head2[N];

inline void add_edge(int from,int to)
{
	edge[++cnt].next=head[from];
	edge[cnt].to=to;
	head[from]=cnt;
}

inline void add_edge2(int from,int to)
{
	edge2[++cnt2].next=head2[from];
	edge2[cnt2].to=to;
	head2[from]=cnt2;
}

template<class T>inline void read(T &res)
{
	char c;T flag=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
	while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}

queue<int> q;
bool vis[N];
void spfa()
{
	for(register int i=1;i<=n;++i) q.push(i),vis[i]=1;
	while(!q.empty())
	{
		int u=q.front();q.pop();vis[u]=0;
		ll res=a[u];
		for(register int i=head[u];i;i=edge[i].next)
		{
			int v=edge[i].to;
			res+=dis[v];
		}
		if(dis[u]>res)
		{
			dis[u]=res;
			for(register int i=head2[u];i;i=edge2[i].next)
			{
				int v=edge2[i].to;
				if(!vis[v])
				{
					q.push(v);
					vis[v]=1;
				}
			}
		}
	}
}

int main()
{
	read(n);
	for(register int i=1;i<=n;++i)
	{
		read(a[i]);
		read(dis[i]);
		read(k);
		while(k--)
		{
			read(x);
			add_edge(i,x);
			add_edge2(x,i);
		}
	}
	spfa();
	printf("%lld\n",dis[1]);
	return 0;
}
posted @ 2019-10-23 21:53  tqr06  阅读(122)  评论(0编辑  收藏  举报