• 【bzoj3875】[Ahoi2014&Jsoi2014]骑士游戏 Spfa优化dp


    题目描述

    有$n$个怪兽,每个怪兽可以花费$k_i$的代价消灭,或者花费$s_i$的代价将其变为$r_i$个给定的新的怪兽。问消灭1号怪兽的最小代价。

    输入

    第一行包含一个整数N。
    接下来N行,每行描述一个怪兽的信息;
    其中第i行包含若干个整数,前三个整数为Si,Ki和Ri,表示对于i号怪兽,
    普通攻击需要消耗Si的体力,法术攻击需要消耗Ki的体力,同时i号怪兽死亡后会产生Ri个新的怪兽。表示一个新出现的怪兽编号。同一编号的怪兽可以出现多个。

    输出

    输出一行一个整数,表示最少需要的体力值。

    样例输入

    4
    4 27 3 2 3 2
    3 5 1 2
    1 13 2 4 2
    5 6 1 2

    样例输出

    26


    题解

    Spfa优化dp

    设$f[i]$表示消灭$i$号怪兽的最小代价,那么显然有状态转移方程$f[i]=min{k[i],s[i]+sumlimits_{i o x}f[x]}$。

    但是这个转移是有环的,因此爆搜是指数级= =

    考虑优化dp:设$g[i]$表示$i$用于更新其它点的代价,那么当一个点的$g[i]>f[i]$时,便可以使能变成它的其它点的$f$值减小$g[i]-f[i]$。这个过程类似于最短路,因此可以使用Spfa来优化这个过程。

    初始时$f[i]=k[i]$,$g[i]=s[i]+sumlimits_{i o x}k[x]$,然后转移即可。

    时间复杂度$O(Spfa)=O(能过)$

    另外本题可以使用类似于堆优化Dijkstra的优化dp方法解决,参见 Lijinnn's Blog

    #include <queue>
    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #define N 200010
    #define M 1000010
    using namespace std;
    typedef long long ll;
    queue<int> q;
    int head[N] , to[M] , next[M] , cnt , inq[N];
    ll v[N] , f[N] , g[N];
    inline char nc()
    {
    	static char buf[100000] , *p1 , *p2;
    	return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ;
    }
    inline int read()
    {
    	int ret = 0; char ch = nc();
    	while(!isdigit(ch)) ch = nc();
    	while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ '0') , ch = nc();
    	return ret;
    }
    inline ll llread()
    {
    	ll ret = 0; char ch = nc();
    	while(!isdigit(ch)) ch = nc();
    	while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ '0') , ch = nc();
    	return ret;
    }
    inline void add(int x , int y)
    {
    	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
    }
    int main()
    {
    	int n = read() , i , m , x;
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		v[i] = llread() , g[i] = llread() , m = read() , inq[i] = 1 , q.push(i);
    		while(m -- ) add(read() , i);
    	}
    	for(x = 1 ; x <= n ; x ++ )
    	{
    		f[x] += v[x];
    		for(i = head[x] ; i ; i = next[i]) f[to[i]] += g[x];
    	}
    	while(!q.empty())
    	{
    		x = q.front() , q.pop() , inq[x] = 0;
    		if(f[x] >= g[x]) continue;
    		for(i = head[x] ; i ; i = next[i])
    		{
    			f[to[i]] -= g[x] - f[x];
    			if(!inq[to[i]]) inq[to[i]] = 1 , q.push(to[i]);
    		}
    		g[x] = f[x];
    	}
    	printf("%lld
    " , f[1]);
    	return 0;
    }
    

     

  • 相关阅读:
    Java注解
    java反射简单入门
    java泛型反射
    BeanUtils.populate的作用
    适配器模式
    原型模式
    抽象工厂模式
    工厂方法模式
    建造者模式
    单例模式
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7721826.html
Copyright © 2020-2023  润新知