BZOJ 3875: [Ahoi2014&Jsoi2014]骑士游戏 dp spfa

3875: [Ahoi2014&Jsoi2014]骑士游戏

Time Limit: 30 Sec  Memory Limit: 256 MB
Submit: 933  Solved: 475
[Submit][Status][Discuss]

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<=2*10^5,1<=Ri,Sigma(Ri)<=10^6,1<=Ki,Si<=5*10^14



Fi=min(Ki,Si+Rjj=1FPj)

如果是DAG的话就随便搞

可是这个不是DAG。。。

那么怎么解决这种具有同层后效性的dp呢(即dp可以同层转移)

用spfa迭代同层后效性dp

应对转移关系建立图论模型,跑spfa进行多次迭代,将所有可以松弛的的都推到队列里就好了


#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<set>
#include<map>
using namespace std;

typedef long long ll;

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}
inline void print(ll x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=200100;

int last[N][2],ecnt[2];
struct EDGE{int to,nt;}e[N<<4][2];
inline void add(int k,int u,int v)
{e[++ecnt[k]][k]=(EDGE){v,last[u][k]};last[u][k]=ecnt[k];}

int n;

ll S[N],K[N],f[N];

int q[N];
bool inq[N];

void spfa()
{
	register int i,u,head=0,tail=0;
	for(i=1;i<=n;++i)inq[i]=1,q[tail++]=i;
	while(head^tail)
	{
		u=q[head++];head%=N;inq[u]=0;
		ll tmp=S[u];
		for(i=last[u][0];i;i=e[i][0].nt)tmp+=f[e[i][0].to];
		if(tmp<f[u])
		{
			f[u]=tmp;
			for(i=last[u][1];i;i=e[i][1].nt)
			if(!inq[e[i][1].to])
			{
				q[tail++]=e[i][1].to;tail%=N;
				inq[e[i][1].to]=1;
			}
		}
	}
}

int main()
{
	n=read();
	register int i,j,v;
	for(i=1;i<=n;++i)
	{
		S[i]=read();K[i]=read();j=read();
		while(j--){v=read();add(0,i,v);add(1,v,i);}
	}
	for(i=1;i<=n;++i)f[i]=K[i];
	spfa();
	print(f[1]);puts("");
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值