• P4042 [AHOI2014/JSOI2014]骑士游戏


    题目描述

    在这个游戏中,JYY一共有两种攻击方式,一种是普通攻击,一种是法术攻击。两种攻击方式都会消耗JYY一

    些体力。采用普通攻击进攻怪兽并不能把怪兽彻底杀死,怪兽的尸体可以变出其他一些新的怪兽,注意一个

    怪兽可能经过若干次普通攻击后变回一个或更多同样的怪兽;而采用法术攻击则可以彻底将一个怪兽杀死。

    当然了,一般来说,相比普通攻击,法术攻击会消耗更多的体力值(但由于游戏系统bug,并不保证这一点)。

    游戏世界中一共有 \(N\) 种不同的怪兽,分别由1到 \(N\) 编号,现在1号怪兽入侵村庄了,JYY想知道,

    最少花费多少体力值才能将所有村庄中的怪兽全部杀死呢?

    说明/提示

    首先用消耗4点体力用普通攻击,然后出现的怪兽编号是2,2和3。花费10点体力用法术攻击杀死两个

    编号为2的怪兽。剩下3号怪兽花费1点体力进行普通攻击。此时村庄里的怪兽编号是2和4。

    最后花费11点体力用法术攻击将这两只怪兽彻底杀死。一共花费的体力是4+5+5+1+5+6=26。

    对于所有数据 \(2 \le N \le 2 \times 10^5,1 \le R_i,\sum R_i \le 10^6,1 \le K_i,S_i \le 5 \times 10^{14}\)

    这道题,我们很容易就想到转移方程

    \(f_i\) 表示消灭第 \(i\) 只怪兽的最小代价

    那么转移方程就可以写成 \(f_i = min(k_i,s_i+\displaystyle\sum f[R[i][j])\)

    可是这会出现环也就是有后效性的dp,我们就需要一种方法来处理这个。

    当后面的 \(k_i\) 变小时,那么前面的 \(f_i\)也会发生变化。

    这不就类似于spfa的松弛操作吗?

    于是,我们就可以每次求完一个点的\(f\)值后,如果他的\(f_i\)变小,那我们需要将能变成它的怪兽在重新入队

    在进行更新,直到所有的点都不能更新为止。

    但一般的最短路都是有起点和终点的,这个题我们无法确定从那个点开始更优。

    所以,我们干脆一开始把所有点都加入队列中,都进行更新。

    以上就是我们spfa处理有后效性dp的思路

    Code

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<queue>
    using namespace std;
    #define int long long//不开long long 见祖宗
    const int N = 2e5+10;
    int n,tot,cnt,num,x;
    int s[N],dis[N],head[N],hed[N];
    bool vis[N];
    inline int read()
    {
    	int s = 0, w = 1; char ch = getchar();
    	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
    	return s * w;
    }
    queue<int> q;
    struct node{int to,net;} e[1000010],e2[1000010];
    void add(int x,int y)
    {
    	e[++tot].to = y;
    	e[tot].net = head[x];
    	head[x] = tot;
    }
    void Add(int x,int y)
    {
    	e2[++cnt].to = y;
    	e2[cnt].net = hed[x];
    	hed[x] = cnt;
    }
    signed main()
    {
    	n = read();
    	for(int i = 1; i <= n; i++)
    	{
    	    s[i] = read(); dis[i] = read(); num = read();//一开始杀死一只怪兽的代价为魔法攻击消耗的体力值
    	    for(int j = 1; j <= num; j++)
    	    {
    	    	x = read(); add(i,x); Add(x,i);
    	    } 
    	}
    	for(int i = 1; i <= n; i++) q.push(i), vis[i] = 1;//一开始把所有的点都进入队列中
            while(!q.empty())
            {
        	      int t = q.front(); q.pop(); vis[t] = 0; int tmp = s[t];
        	      for(int i = head[t]; i; i = e[i].net)//计算把他变成其他怪兽,并将它们消灭的最小代价
        	      {
        		 int to = e[i].to;
        		 tmp += dis[to];
        	      }
        	      if(tmp <= dis[t])//如果杀死他的代价变小了,我们就要把能变成他的怪兽都重新入队在进行次更新
        	      {
        		  dis[t] = tmp;
        		  for(int i = hed[t]; i; i = e2[i].net)
        		  {
        		      int to = e2[i].to;
        		      if(!vis[to])
        		      {
        		         q.push(to); 
        			 vis[to] = 1;
        		      }
        		   }
        	        }
              }
    //    for(int i = 1; i <= n; i++) cout<<dis[i]<<endl;
          printf("%lld\n",dis[1]);
        return 0;
    }
    

    ENDING

  • 相关阅读:
    Pycharm使用
    解决TortoiseGit下载代码每次要输入用户名、密码
    GitLab创建项目
    【编码格式错误】SyntaxError: Non-UTF-8 code starting with
    C 位段,位域
    跳跃表 -- 随机平衡原理
    PHP 中的新语法 new static 是个啥意思?
    位运算之——按位与(&)操作——(快速取模算法)
    Redis Scan迭代器遍历操作原理(一)
    Redis Scan迭代器遍历操作原理(二)
  • 原文地址:https://www.cnblogs.com/genshy/p/13527120.html
Copyright © 2020-2023  润新知