bzoj3875: [Ahoi2014&Jsoi2014]骑士游戏(用spfa解决有后效性的dp)

www.cnblogs.com/shaokele/


bzoj3875: [Ahoi2014&Jsoi2014]骑士游戏##

  Time Limit: 30 Sec
  Memory Limit: 256 MB

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
  

Sample Output###

  26
  

HINT###

  【样例说明】
  
  首先用消耗4点体力用普通攻击,然后出现的怪兽编号是2,2和3。花费
  
  10点体力用法术攻击杀死两个编号为2的怪兽。剩下3号怪兽花费1点体力进
  
  行普通攻击。此时村庄里的怪兽编号是2和4。最后花费11点体力用法术攻击
  
  将这两只怪兽彻底杀死。一共花费的体力是4+5+5+1+5+6=26。
  
  【数据范围】
  
  2<=N<=2105,1<=Ri,Sigma(Ri)<=106,1<=Ki,Si<=510^14
  

题目地址:  bzoj3875: [Ahoi2014&Jsoi2014]骑士游戏

题目大意:

  给定n个怪物,每个怪物可以直接⽤魔法⼲掉,或者⽤物理攻击使其分裂成⼀些其他的怪物,求杀掉1号怪物的最⼩代价。

题解:

  我们可以令f[i]表示杀死i怪兽的最⼩代价。
   \(f[i]=min(k[i],s[i]+sigma(f[j]))\) \(j\) \(i\) 受到物理攻击后分裂成的所有怪兽。
  我们发现直接 dp 是有后效性的,我们可以⽤ SPFA 来做这个DP。
  ⽤两个数组每次更新差值就好了。
  时间复杂度 \(O(能过)\)


AC代码

#include <cstdio>
#include <cstring>
#include <queue>
#define ll long long
using namespace std;
const int N=2e5+5,M=1e6+5;
int n,cnt1,cnt2,a[N],last1[N],last2[N];
ll dis[N];
inline ll read(){
	ll x=0;int f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
struct edge{
	int to,next;
}e1[M],e2[M];
void add_edge1(int u,int v){
	e1[++cnt1]=(edge){v,last1[u]};last1[u]=cnt1;
}
void add_edge2(int u,int v){
	e2[++cnt2]=(edge){v,last2[u]};last2[u]=cnt2;
}
queue<int> q;
void spfa(){
	bool inq[N];memset(inq,0,sizeof(inq));
	while(!q.empty())q.pop();
	for(int i=1;i<=n;i++)
		q.push(i),inq[i]=1;
	while(!q.empty()){
		int u=q.front();q.pop();inq[u]=0;
		ll sum=a[u];
		for(int i=last1[u];i;i=e1[i].next)
			sum+=dis[e1[i].to];
		if(sum>=dis[u])continue;
		dis[u]=sum;
		for(int i=last2[u];i;i=e2[i].next)
			if(!inq[e2[i].to])
				q.push(e2[i].to),inq[e2[i].to]=1;
	}
}
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i]=read();dis[i]=read();int num=read();
		while(num--){
			int x=read();
			add_edge1(i,x);
			add_edge2(x,i);
		}
	}
	spfa();
	printf("%lld\n",dis[1]);
	return 0;
}
posted @ 2018-07-26 15:20  skl_win  阅读(300)  评论(0编辑  收藏  举报
Live2D