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\n",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那个区间的所有相等的值、

*/
posted @ 2018-04-12 18:22  KuroNekonano  阅读(132)  评论(0编辑  收藏  举报