Codeforces 1334C - Circle of Monsters(差值取前缀和 / 贪心)

两种思路其实只差在写法上

看不懂的就直接看代码吧qwq

题面

题意

n只怪物围成一圈,每只怪物拥有体力a和爆炸伤害b

如果怪物 i 死亡(体力小于等于0),则与他相邻的下一只怪物将受到 b[i] 点伤害

(如果 i<n ,则下一只怪物为 i+1 ;如果 i==n,则下一只怪物为 1)

如果相邻的下一只怪物不存在,则什么也不会发生

如果相邻的下一只怪物受到伤害后也死亡了,那么再下一只怪物会继续受到伤害(链式反应)

每次你能随便挑一只怪物开一枪,那只怪物的体力将会降低 1 点

问至少需要开多少枪才能解决掉所有怪物

解题思路 1

可以知道,只要挑出一只怪物作为最开始杀的那只,那接下来n-1只就按顺序杀下来就可以了

所以问题最主要的就是找出最开始的这一只怪物

又因为 n 最大有 3e5,每一次模拟都是O(n)的复杂度,总体时间复杂度O(n2)是绝对不可行的

想了一个多小时才想到 于是发现可以用循环数组存前缀和来模拟出所有情况的答案

但循环数组写起来太麻烦了,干脆直接开两倍长度的数组(3个6e5的long long不需要担心MLE)

读入数据存在ar和br两个数组中

并且让ar[i] == ar[i+n] && br[i] == br[i+n] ,i=1~n

再开一个 used 数组,used[i] 记录从编号为 1 的怪物杀到编号为 i 怪物的过程中需要用掉多少颗子弹

注意used只记录过程中,不记录杀编号为 1 的怪物用掉的子弹,处理过程如下:

  used[1] = 0 特殊处理

  i 从2开始,如果第 i 个怪物体力小于等于第 i-1 个怪物的爆炸伤害,说明只要前一只怪物死亡那这一只也会跟着死亡,对答案无贡献 used[i] = used[i-1]

  如果第 i 个怪物体力大于第 i-1 个怪物的爆炸伤害,说明还需要补上 ar[i] - br[i-1] 颗子弹才会死亡 used[i] = used[i-1] + ar[i] - br[i-1]

因为原本是个循环数组,所以used需要处理到2*n过(实际上只要2*n-1)

全部处理完后,那么所有情况就出来了

比如说我们选择以 i=1 的怪物作为开始

那么首先花费 ar[1] 颗子弹把这只怪物杀死

然后从第1只开始杀n只,最后一只怪物的编号为 i=n

所以过程中还需要多花费 used[n] - used[1] 颗子弹

所以这种情况的答案就是 ar[1] + used[n] - used[1]

推广,对于 i∈[1,n] 的每种情况,答案为 ar[i] + used[i+n-1] - used[i]

取小输出即可

程序 1

把处理used数组的步骤和计算答案的步骤合起来

则处理答案的范围为 n~2*n-1

(280ms/1000ms)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll LINF=0x3f3f3f3f3f3f3f3f;

ll ar[600050],br[600050],used[600050];
void solve()
{
    int n;
    cin>>n;
    ll ans=LINF;
    for(int i=1;i<=n;i++)
    {
        cin>>ar[i]>>br[i];
        ar[n+i]=ar[i];
        br[n+i]=br[i];
    }
    used[1]=0;
    for(int i=2;i<2*n;i++)
    {
        if(ar[i]<=br[i-1])
            used[i]=used[i-1];
        else
            used[i]=used[i-1]+ar[i]-br[i-1];
        if(i>=n)
            ans=min(ans,used[i]-used[i-n+1]+ar[i-n+1]);
    }
    cout<<ans<<‘\n‘;
}
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int T;cin>>T;
    for(int t=1;t<=T;t++)
        solve();
    return 0;
}

解题思路 2

可以发现,思路1中的used[i+n] - used[i]其实是一个定值(注意不是 i+n-1 和 i )

这个定值表示的就是忽略杀掉第一只怪物的花费,在过程中总共需要花费的子弹数

令这个定值为 k ,k的求法即为k = k + sum{max(0,ar[i]-br[i-1])}

有了这个定值,我们只需要找出杀第一只怪物最少的花费与其相加就是答案了

令杀第一只怪物最少的花费为 minn

与思路1的式子联立答案方程能得到

used[i+n] - used[i] + minn == used[i+n-1] - used[i] + ar[i]

ar[i] - minn == used[i+n] - used[i+n-1] == used[i] - used[i-1]

所以可以知道 ar[i] 与 minn 的差值就在于 used[i] - used[i-1] 这一步

而这个差值也就代表着某一步需要多消耗的子弹数

得到minn == ar[i] - (used[i] - used[i-1])

对于第 i 只怪物:

如果br[i-1] >= ar[i]

  说明前一只死亡后会顺带让第 i 只怪物死亡

  所以在 k 的计算过程中并没有把杀死第 i 只怪物的花费记录进去

  即代表此时的used[i] - used[i-1] == 0

  所以此时可以得到 minn = min(minn,ar[i])

如果br[i-1] < ar[i]

  说明前一只死亡后并不会顺带让第 i 只怪物死亡

  所以在 k 的计算过程中会加入 ar[i] 和 br[i-1] 的差值作为需要补的子弹数

  即代表此时的used[i] - used[i-1] == ar[i] - br[i-1]

  所以此时可以得到 minn = min(minn,br[i-1])

最后再判断下编号为1和编号为n的这两只的关系

程序 2

(280ms/1000ms)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll LINF=0x3f3f3f3f3f3f3f3f;

ll ar[300050],br[300050];
void solve()
{
    int n;
    ll k=0,minn=LINF,d;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>ar[i]>>br[i];
        d=ar[i]-br[i-1];
        if(i)
        {
            if(d>0)
                minn=min(minn,br[i-1]),k+=d;
            else
                minn=min(minn,ar[i]);
        }
    }
    d=ar[0]-br[n-1];
    if(d>0)
        minn=min(minn,br[n-1]),k+=d;
    else
        minn=min(minn,ar[0]);
    cout<<k+minn<<‘\n‘;
}
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int T;cin>>T;
    for(int t=1;t<=T;t++)
        solve();
    return 0;
}

原文地址:https://www.cnblogs.com/stelayuri/p/12677416.html

时间: 2024-11-07 20:28:10

Codeforces 1334C - Circle of Monsters(差值取前缀和 / 贪心)的相关文章

Houdini中给火花渲染准确的运动模糊 - 给运动模糊做非线性差值的方法以及固定粒子点数的方法

估计大家都知道使用运动速度来进行运动模糊的渲染,但是往往这个方法得到的运动模糊都是线性变化的,虽然乍一看没什么问题,但是如果想要每一帧的模糊轨迹也是有曲线变化的而不是僵硬的直来直去的话,使用trail算个速度来做的运动模糊是永远做不到这一点的. 这里我想通过常用的火花(spark)的运动模糊来讲一讲我所了解的一些比较好的方法. 所谓渲染中的运动模糊无非就是差值算法.目前使用的比较多的主要有两种.第一种就是上面说到的直接使用速度来线性差值,这种方法会计算每一个点的速度方向,计算出前一帧或者后一帧的

CodeForces 444C 节点更新求变化值的和

http://vjudge.net/problem/viewProblem.action?id=51622 题目大意: 给定一列n个数字,最初赋予值1到n 两个操作:1.将区间[l,r]内的数改为x,则这区间中所有数的改变值进行求和,即ans=abs(a[l]-x)+abs[a[l+1]-x).....abs(a[r]-x),但不要求输出 2.需要将刚才要求得到的区间改变值输出出来 这里我们利用一个color[]数组相当于给这堆数进行染色,当某个区间内的赋予的值相等时,我们可以看做有一个相同的c

获取两个字符串日期的差值的方法

日期的格式:“yymmddhhmmss”是一个字符串,计算两个日期之间的差值,显然就是计算两个日期之间相差的秒数,有个简洁的方法是将字符串转化为time_t格式,用time_t表示的时间(日历时间)是从一个时间点(例如:1970年1月1日0时0分0秒)到此时的秒数 我们可以看到它的定义是这样的 #ifndef _TIME_T_DEFINEDtypedef long time_t;           /* 时间值 */#define _TIME_T_DEFINED       /* 避免重复定义

什么是差值查找?

1.插值查找与二分查找很类似,都是用于在有序的基础上查找某个元素 2.二分查找的原理是,每次都取一半,然后与mid值比较,再决定下一次查找的范围 3.设想在一本英文字典里查找某个单词,因为是根据字母序排列好的,你不会傻到采用二分查找的方法,先找到这本字典的一半,再取这本字典的四分之一...这样下去比较吧,这种效率显然不是最快的,此时就轮到我们的差值查找出场了. 4.差值公式的推导: 有序序列: 1 2,3,4,5,6,7,8,9,10 比例公式:   假设数组a[i]已经有序 (key-low)

动态集合中两个最接近的数的差值

题目: 思考: 既然是动态集合,那么我们需要用链表来存储数据方便插入和删除.于是我们可以选用二叉链表,也就是红黑树来存储数据,红黑树由于比较平衡,所以可以得到比较好的查询时间.但是我们并不是直接拿红黑树就可以用了,因为基本的红黑树没有MIN_GAP操作,所以需要自己修改和维护原始的红黑树. 如何给红黑树添加MIN_GAP操作呢?我们需要先给红黑树结构中加入MAX,MIN指针.这个操作可以参考最坏时间为O(1)的求最大小值,当然只取了里面部分新增代码.既然新指针已经添加好了,那么我们以原有红黑树插

n个数分为两组,两组数的个数尽可能相等,差值最小

题目描述:对于有n个数的数组,分为两组,这两组的数的个数尽可能相等(不超过1),同时两组的数之和的差值最小. 这个题目使用类似0-1背包问题,思路:从k个数中选i个数,求所有可能的和,并把这些和放在flag中用true表示.(k,i,flag见代码) 1 public static void main(String[] args){ 2 int[] arr = {1 , 2 , 3 , 5 , 7 , 8 , 9}; 3 int n = 7; 4 int sum = 0; 5 for(int i

阿里 2014-08-29 校招机试题 求一个存放整数的二叉树相差最大的两节点差值绝对值

题目:写一个函数,输入一个二叉树,树中每个节点存放了一个整数值,函数返回这颗二叉树中相差最大的两个节点间的差值绝对值.请注意程序效率. 如果是数值之差,感觉怎么着也得遍历一遍,直接修改下二叉树的基本遍历代码就可以. #include<stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node * left; struct Node * right; } BitNode, *BiTree; /* 求

查找(哨兵查找、二分查找、差值查找)

#include <iostream> using namespace std; #define N 10 int fib(int n) { if(n == 0) { return 0; } else if(n == 1) { return 1; } else { return (fib(n-1) + fib(n-2)); } } //普通查找: int sequenctial_Search(int *a,int n,int key) { int i; a[0] = key; i = n; w

华为机试—差值排序

对整形数组按照和指定整数的差值大小进行排序,按照差值升序排列返回. 要求实现方法: public staticint[] calcTimes(int[] num, int value); [输入] num:整型数组: value 指定的整数 [返回] 按照升序返回整型数组,排序按照各个整数和指定整数的差值大小 [注意]只需要完成该函数功能算法,中间不需要有任何IO的输入输出 示例 输入:num = {1,2,3,4,5,10,17,18,19}  value = 5 返回:{5,4,3,2,1,