BZOJ 3875 Ahoi2014 骑士游戏

3875: [Ahoi2014]骑士游戏

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

  这真的是一道很妙的题,反正我是没做出来的。

  困扰在"一个怪被砍死会长出一堆不同的怪"这个问题上,难道怪物基因都是相同的怎么跑最短路啊。

  去网上看了一波题解发现都讲的很少,似乎是因为这题太水?智障选手请过早弃疗。

  假设没有环出现,对它是一个DAG,就是一个很水的DP题了,反向拓扑一下。

  然后现在有环了。有环就会有什么呢?有后效性。DP是不能解决有后效性的问题的。

  但是SPFA可以。SPFA就是完全没有管后效性的算法(所以很容易被卡?)。

  设把怪净化掉的最小花费是f。那么就有: f[i]=min(K[i],S[i]+Σf[R]);

  所以一个家伙被更新可能会造成前驱的更新。

  所以把它的前驱扔进队列直到队列为空输出f[1]是什么算法啊啊啊啊!

  咳咳。所以具体实现步骤就是这样:

  把所有的点的f赋为K[i]并加入队列。

  计算队首的f‘。如果比当前优秀,就更新并加入所有前驱。

  直到队列空。

  其实我很害怕这个东西的复杂度,但是就是过了... ...

#include    <iostream>
#include    <cstdio>
#include    <cstdlib>
#include    <algorithm>
#include    <vector>
#include    <cstring>
#include    <queue>
#include    <complex>
#include    <stack>
#define LL long long int
#define dob double
using namespace std;

const int N = 200010;
const int M = 1000010;
struct Node{int to,next;}E[M];
int head[N],tot,n,In[N],R[N];
LL S[N],f[N],K[N];
vector<int>G[N];

int gi()
{
  int x=0,res=1;char ch=getchar();
  while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)res*=-1;ch=getchar();}
  while(ch<=‘9‘&&ch>=‘0‘)x=x*10+ch-48,ch=getchar();
  return x*res;
}

LL gL()
{
  LL x=0,res=1;char ch=getchar();
  while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)res*=-1;ch=getchar();}
  while(ch<=‘9‘&&ch>=‘0‘)x=x*10+ch-48,ch=getchar();
  return x*res;
}

inline void link(int u,int v){
  E[++tot]=(Node){v,head[u]};
  head[u]=tot;
}

inline void SPFA()
{
  queue<int>Q;for(int i=1;i<=n;++i)Q.push(In[i]=i);
  while(!Q.empty()){
    int x=Q.front();Q.pop();In[x]=0;LL sum=S[x];
    for(int i=0;i<R[x];++i)sum+=f[G[x][i]];
    if(sum>=f[x])continue;f[x]=sum;
    for(int e=head[x];e;e=E[e].next)
      if(!In[E[e].to])Q.push(E[e].to),In[E[e].to]=1;
  }
}

int main()
{
  n=gi();
  for(int i=1;i<=n;++i){
    S[i]=gL();K[i]=gL();R[i]=gi();
    for(int j=1;j<=R[i];++j){
      int r=gi();
      link(r,i);G[i].push_back(r);
    }
    f[i]=K[i];
  }
  SPFA();
  printf("%lld\n",f[1]);
  return 0;
}

  

时间: 2024-12-18 00:31:54

BZOJ 3875 Ahoi2014 骑士游戏的相关文章

BZOJ 3875 Ahoi2014 骑士游戏 SPFA

题目大意:给定n个怪物,每个怪物可以用魔法直接干掉,或者用物理攻击使其分裂为一些其他怪物,求杀掉1号怪物的最小花销 令f[i]为杀死i号怪物的最小花销,则f[i]=min(k[i],s[i]+Σf[j]) 其中j为i用物理攻击后可以分裂为的怪物 但是直接DP有后效性,因此我们用SPFA来跑这个DP即可 注意如果每次更新一个点之后都重新计算花销会T掉 改成减掉花销的差值就好了 具体写法去看代码吧- - #include <cstdio> #include <cstring> #inc

bzoj3875 [Ahoi2014]骑士游戏

Description [故事背景] 长期的宅男生活中,JYY又挖掘出了一款RPG游戏.在这个游戏中JYY会扮演一个英勇的骑士,用他手中的长剑去杀死入侵村庄的怪兽. [问题描述] 在这个游戏中,JYY一共有两种攻击方式,一种是普通攻击,一种是法术攻击.两种攻击方式都会消耗JYY一些体力.采用普通攻击进攻怪兽并不能把怪兽彻底杀死,怪兽的尸体可以变出其他一些新的怪兽,注意一个怪兽可能经过若干次普通攻击后变回一个或更多同样的怪兽:而采用法术攻击则可以彻底将一个怪兽杀死.当然了,一般来说,相比普通攻击,

[BZOJ3875][AHOI2014]骑士游戏(松弛操作)

题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3875 分析: 类似于spfa求最短路,设d[i]表示完全消灭i号怪物的最小花费,我们对d[]进行动态更新 我们可以把问题反向:一开始所有怪物都存活,我们要找到一个怪物合成方案合成1号怪物,其中合成方法有两种,一种是魔法攻击逆向产生怪物,另一种就是由某个怪物的后继怪物合成此怪物 对于第一种产生方法的表示,令初始的d[i]=k[i]即可 对于第二种产生方法的表示,也就是算法的核心——类似

【BZOJ3875】[Ahoi2014]骑士游戏【最短路】【DP】

[题目链接] 对于怪物u,普通攻击打死后产生的怪物为vi.设dis[u]表示打死u的最小花费,那么有 dis[u] = min{s[u] + ∑dis[vi], k[u]} 以这个为松弛条件,跑spfa就可以啦. 然而BZOJ跑了29s...变为倒数rank1 /* Telekinetic Forest Guard */ #include <cstdio> #include <cstring> #include <algorithm> using namespace s

「AHOI2014/JSOI2014」骑士游戏

「AHOI2014/JSOI2014」骑士游戏 传送门 考虑 \(\text{DP}\). 设 \(dp_i\) 表示灭种(雾)一只编号为 \(i\) 的怪物的代价. 那么转移显然是: \[dp_i = \min(K_i, S_i + \sum_{j = 1}^{R_i} dp_{v_j})\] 但是我们会发现这个东西是有后效性的... 所以我们会想要用建图然后跑一个最短路什么的来搞... 于是我们观察到上面那个 \(\text{DP}\) 式子中,\(dp_i\) 如果用后面那一项来转移,显然

BZOJ 2756 奇怪的游戏(最大流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2756 题意:在一个 N*M 的棋盘上玩,每个格子有一个数.每次 选择两个相邻的格子,并使这两个数都加上 1. 问最少多少次能使棋盘上的数都变成同一个数,如果永远不能变成同一个数则输出-1. 思路:对棋盘进行黑白染色,则每次操作使得黑白两色的格子总和各增加1.设黑色总和s1,个数cnt1:白色总和s2,个数cnt2,设最后的数字为x,那么有: x*cnt1-s1=x*cnt2-s2. (

BZOJ 1413 取石子游戏(DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1413 题意:n堆石子排成一排.每次只能在两侧的两堆中选择一堆拿.至少拿一个.谁不能操作谁输. 思路:参考这里. int f1[N][N],f2[N][N],n,a[N]; void deal() { RD(n); int i,j,k; FOR1(i,n) RD(a[i]),f1[i][i]=f2[i][i]=a[i]; int p,q,x; for(k=2;k<=n;k++) for(

BZOJ 1978 取数游戏(DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1978 题意:给出一个数列a,在其中找出下标依次增大的数,使得任意相邻的两个数的最大公约数大于等于m.找出最多的数字. 思路:f[i]表示前面的数字中最大公约数为i可以找出的最多的数字个数.那么对于当前数字x: 接着更新f: int f[N],a[N]; int n,m; int main() { RD(n,m); int i; FOR1(i,n) RD(a[i]); int j,k;

bzoj 1059: [ZJOI2007]矩阵游戏 二分图匹配

1059: [ZJOI2007]矩阵游戏 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1891  Solved: 919[Submit][Status] Description 小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏.矩阵游戏在一个N*N黑白方阵进行(如同国际象棋一般,只是颜色是随意的).每次可以对该矩阵进行两种操作:行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)列交换操作:选择矩阵