【bzoj3875】【AHOI2014】【骑士游戏】【spfa+dp】

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
题解:一开始想建个奇怪的图跑最短路。写了一会发现没看到可能分出多个一样的。。
          然后就只会写dp了。
          定义f[i]为杀死i的最小代价.g[i]为用普通攻击攻击i然后杀死i及其分身的最小代价。
          a[i]为用法术攻击攻击i的代价,b[i]为用普通攻击攻击i的代价。
          每个怪物向它的分身连边,然后需要建一个正着的图和一个反着的图。
          用正着的图进行初始化。f[i]=a[i], g[i]=sigma(f[j])(i和j相连)+b[i];
          然后在反图上dp.方程随便yy就好了。然后在spfa上做这个dp就好了。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200010
using namespace std;
struct use{int st,en;}e[N*40];
long long s[N],k[N],f[N],g[N];
int l[N*40],point1[N],point2[N],next[N*40],n,m,cnt,x,p,h,t;
bool vis[N];
void add(int point[],int x,int y){
    next[++cnt]=point[x];point[x]=cnt;
    e[cnt].st=x;e[cnt].en=y;
}
void spfa(){              
    while (h<t){
     int u=l[++h];vis[u]=false;if (g[u]>=f[u]) continue;
     for (int i=point2[u];i;i=next[i]){
        g[e[i].en]-=f[u];g[e[i].en]+=g[u];
        if (!vis[e[i].en]) {l[++t]=e[i].en;vis[e[i].en]=true;}
     }
     f[u]=g[u];
    }   
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
     l[++t]=i;vis[i]=true;
     scanf("%lld%lld%d",&s[i],&k[i],&p);
     for (int j=1;j<=p;j++){scanf("%d",&x);add(point1,i,x);add(point2,x,i);}
    }
    for (int i=1;i<=n;i++){
      f[i]=k[i];g[i]=s[i];
      for (int j=point1[i];j;j=next[j]) g[i]+=k[e[j].en];
    }
    spfa();
    cout<<f[1]<<endl;
}


          
          
     
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值