• UPC-5594 Colorful Slimes(思维)


    题目描述
    Snuke lives in another world, where slimes are real creatures and kept by some people. Slimes come in N colors. Those colors are conveniently numbered 1 through N. Snuke currently has no slime. His objective is to have slimes of all the colors together.

    Snuke can perform the following two actions:

    Select a color i (1≤i≤N), such that he does not currently have a slime in color i, and catch a slime in color i. This action takes him ai seconds.

    Cast a spell, which changes the color of all the slimes that he currently has. The color of a slime in color i (1≤i≤N−1) will become color i+1, and the color of a slime in color N will become color 1. This action takes him x seconds.

    Find the minimum time that Snuke needs to have slimes in all N colors.

    Constraints
    2≤N≤2,000
    ai are integers.
    1≤ai≤109
    x is an integer.
    1≤x≤109
    输入
    The input is given from Standard Input in the following format:

    N x
    a1 a2 … aN
    输出
    Find the minimum time that Snuke needs to have slimes in all N colors.
    样例输入
    2 10
    1 100
    样例输出
    12
    提示
    Snuke can act as follows:

    Catch a slime in color 1. This takes 1 second.
    Cast the spell. The color of the slime changes: 1 → 2. This takes 10 seconds.
    Catch a slime in color 1. This takes 1 second.

    题意:给出一段序列,可以由两种操作,1,用序列中某个数值的花费直接买到那个数值,2,将已经获得的一些数值按原序列顺序集体后挪一个值,花费x值。如取到1,1后面是100,x是10,则先取1,再花费10,可以使1变成100。问如何花费最小凑够给出的序列。

    首先找到序列中最小值,因为这个最小值可以通过挪动变成序列中任何值,其二,因此后挪操作是已获得序列全体后挪,那么已获得序列的值越多,后挪的数越多,获得的值越多,那么可能通过后挪节省的花费就越多。那么我们首先向,求出一个sum表示若所有值都用直接购买的方式获得,值是多少。这个值即最大花费,然后再用这个值,通过后挪操作看看具体可以减少多少花费。对于两两相邻的值,我得到他有两种方法,一是直接购买,那么花费即两者之和a[i]+a[i+1],第二种是先得到前者,再后挪,即a[i]+a[i]+x,我们需要对比两种获得方式的花费谁更小,那么就是作差,得到a[i+1]-a[i]这个值,因为挪动的花费还有x,也就是说,如果a[i+1]-a[i]小于x,即得到这两个数的花费是可以通过挪动减少的。于是我们从最小值的位置开始,一一做两两的对比。若后者大于前者,表示后者可以通过前者挪动得到,显然我们不希望花费两者中更大的那个值的花费。我们应用较小值得到较大值。那么通过前者得到,我们将较大值变成前者。然后继续比较。

    注意的是我们采用的对比顺序是从mini(最小值位置)回头比较,而不是比较其后续。为什么是回头比较而不是向后比较呢?明明题目给出的是后挪操作。因为如果我们向后比较,后面的某个值都是递增的,那么就有可能出现后面连续几个值,都是有当前一个最小值通过后挪得到的,这不要紧,要紧的是是后面第几个值,我们就用当前值后挪了几次,这显然是不妥的,因为我们后挪的操作是对已获得序列的所有值都后挪,而我们这样计算出来的是,放入一个值后挪n次,再放入一个值,再后挪n次,这样的形式明显不符合题意,也不是最优解的操作。
    因此我们逆向思考,将当前值看做是之前的值推过来的,这样的更新会有延迟,能保证我一定是经过一次操作后挪得到的,并且这一次操作没有限定是多少个值,我当前值的获得方式可能是由上一个值后挪一次得到,也可能是由上一段值,集体后挪得到。

    注意经过刚才这样情况的考虑,我们发现,我们任何时候都可以看做是两个数之间做的操作,即:当前值是由上一个数后挪得到,或者是上一个数之前的几个数同时后挪得到,无论是集体后挪还是单个后挪,我们都看作是两个数之间的后挪关系。

    这样对比两数大小并改变值的操作循环执行一圈后,我们记录所有两两数之间的差值之和,记为t,当t大于x时,说明这个差值我们仍可以用x进行优化,为什么是差值之和呢?如果是两数之间的差值我们对x比较可以理解,怎么需要所有差值之和小于x才是最终正解呢?

    因为我们之前说过,任何的挪动都可以看做是一段数或一个数的,我们一开始可以看做是当前值和上一个值的挪动,直到最后,我们可以看做是当前值和剩下所有值这段序列后挪得到。也就是说,我们计算的t,把其叫做差值之和,其实是对整个序列的后挪花费进行了计算。这个t仍表示的是前后两数的挪动消费差值。

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    int main()
    {
        int n,x,a[2005];
        while(scanf("%d%d",&n,&x)!=EOF)
        {
            LL sum=0;///sum求出若所有数都直接取本身的总花费
            int minn=0x7fffffff,mini;
            for(int i=0;i<n;i++)
            {
                scanf("%d",&a[i]);
                sum+=a[i];
                if(a[i]<minn)mini=i,minn=a[i];///找到最小值做遍历起点
            }
            while(true)
            {
                LL dis=0;
                for(int i=0;i<n-1;i++)///多次循环,遍历一圈,当前值大于上一个值说明可以通过挪动优化
                {
                    if(a[(mini-i+n-1)%n]>a[(mini-i+n-2)%n])
                    {
                        dis+=a[(mini-i+n-1)%n]-a[(mini-i+n-2)%n];///求前后两数差和
                        a[(mini-i+n-1)%n]=a[(mini-i+n-2)%n];///将较大数等于前者较小值,说明该数有上一个值挪动而来
                    }
                }
                if(dis<x)break;
                sum-=dis-x;///因为每次挪动是对整段序列,因此求差值也就是可优化值是对所有两两差值的求和,也就是差值和对比x可优化值,若大于,继续优化
            }///用总费用直接减去差值和,从中剃去挪动费用
            printf("%lld
    ",sum);
        }
    }
    /*
    后者花费ai前者花费a(i-1),单独拿来两者花费是ai+a(i-1),
    否则是a(i-1)+a(i-1)+x,因为后者比前者大,所以加和必定
    大于a(i-1)+a(i-1),那么两者之差就是ai-a(i-1),也就是单独
    拿来的花费减去挪动的花费,这个值如果大于x,那么挪动是划
    算的,如果小于x说明这里不需要挪动
    我不能仅考虑两两之间的差值是否小于x,因为是整体挪动。。
    。如果仅是两两差值小于x,求和大于x,必定还可以优化,因为
    我明明可以不是单个移动的,而是整体移动的,那么,你更改成
    相同值的一些数就可以看做是一段区间,看做一个单独的数,我
    其实是在比较当前这个值,和i-1那个区间的所有相等的值、
    
    */
    
  • 相关阅读:
    Windows下利用TortoiseSVN搭建本地SVN服务器
    我的Netty笔记
    Netty简单的HTTP服务器
    开启和关闭HBase的thrift进程
    java中重载和重写的区别
    java中形参个数可变的方法使用
    java中方法的参数传递机制
    Java内存分配全面浅析
    java中的类修饰符、成员变量修饰符、方法修饰符。
    js实现页面重定向
  • 原文地址:https://www.cnblogs.com/kuronekonano/p/11135797.html
Copyright © 2020-2023  润新知