DAY 2 TEST

送分小仙女□ 提交于 2020-02-01 23:00:15

T1

【样例输入1】

3

1 1 100

50 50 50

50 50 50

【样例输出1】

133

32

样例解释:只移动100,将其移动到A中剩余两堆各16次,一共32次,此时100还剩下4,此时由于每次只能恰好移动2,最多在这一堆只能再往外运两次,无论运到哪里,都不会增加I, 所以移动32次.

【数据规模】

对于60%的数据,所有的\(A_i,B_i,C_i\)均为偶数.

对于另20%的数据,\(1 ≤ 𝐴𝑖 , 𝐵𝑖 , 𝐶𝑖 ≤ 2\)

对于100%的数据,\(𝑁 ≤ 100000, 1 ≤ 𝐴𝑖 , 𝐵𝑖 , 𝐶𝑖 ≤ 10^9\)

20pts:枚举每一列运输完之后的最小值是奇数还是偶数。因为每个数只能是1或者2,所以步数可以直接求出来,然后比较优劣

60pts:所有的\(A_i,B_i,C_i\)都是偶数,考虑都除以二变成奇数,然后问题转化成每次移动1。

问题一很简单,就是\(sum/n\),因为不管怎样,把所有的数都变成平均值一定是最优的方案

顺着第一问,首先一个直观的想法是分别求出每一组的平均值,然后按照平均值移动。但是这样是不行的。比如:

10 10 1

1 1 10

这组数据如果按照平均值移动,要把第一组都变成7,移动6次;把第二组变成4,移动6次,一共12次

但是如果我们直接把第二组的10移动9到第一组的1,就只需要9次

不妨先把每一组的数排个序,排好序之后的序列一定是呈阶梯状上升的

那么每次肯定是要把最多的地方填到少的去,使得最小值达到下一个阶梯(比如\(A_1\)填一次达到\(A_2\)),并且保证序列单调递增的性质(也就是不改变后面的阶梯)

由于我们已经知道填补之后的\(ans\),即\(A_1+B_1+C_1\),所以我们只需要每次填补检查一下是否已经满足这个条件就行了

100pts:既有奇数,也有偶数

考虑奇数和偶数是交替的,也就是说如果拿掉2之后,答案既可能-2也可能-1

这里给出的方法是枚举每个序列最后的答案(即最小值)是奇数还是偶数

如果是奇数,那么其他的偶数至少是2,就把奇数-1,偶数-2

如果是偶数,那么其他的奇数至少是3,只把奇数-1

这就转化成了全是偶数的问题

code:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<set>
#include<vector>
#include<map>
#include<queue>

#define N 300005
#define M 8000005

#define ls (t<<1)
#define rs ((t<<1)|1)
#define mid ((l+r)>>1)

#define mk make_pair
#define pb push_back
#define fi first
#define se second

using namespace std;

int i,j,m,n,p,k,A[3][N],B[N],tot;

long long Ans1=0,Ans2=0,temp1,temp2,CT;

struct Node{
        int wid,high;
}C[N];

void Wt(int c)
{
        int i;
        if (!c)
        for (i=1;i<=n;++i) if (B[i]&1) B[i]-=1;
        if (c)
        for (i=1,temp1++;i<=n;++i) if (B[i]&1) B[i]-=1; else B[i]-=2; 
        for (i=1;i<=n;++i) B[i]/=2;
        temp1+=B[1]*2;
        for (i=2;i<=n;++i) C[++tot]=(Node){i-1,B[i]-B[i-1]}; 
        for (i=2;i<=n;++i) CT+=B[i]-B[1];
}

int cmp(Node a,Node b)
{
        return a.wid<b.wid;
}

void Work(int x)
{
        int i,j; tot=0; temp1=temp2=0; CT=0;
        for (i=0;i<3;++i)
        {
            for (j=1;j<=n;++j) B[j]=A[i][j];
            Wt(x&(1<<i));
        } 
        CT/=n;
        sort(C+1,C+tot+1,cmp);
        for (i=1;i<=tot;++i) 
            if (C[i].high<CT)
            {
                    temp2+=1ll*C[i].wid*C[i].high;
                    CT-=C[i].high;
                    temp1+=C[i].high*2;
            }   
            else
            {
                    temp2+=1ll*C[i].wid*CT;
                    temp1+=CT*2;
                    CT=0;
            }
        if (temp1>Ans1||temp1==Ans1&&temp2<Ans2) Ans1=temp1,Ans2=temp2;
}

int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout); 
    scanf("%d",&n);
    for (i=0;i<3;++i) 
    {
        for (j=1;j<=n;++j) scanf("%d",&A[i][j]);
        sort(A[i]+1,A[i]+n+1);
    }
    for (i=0;i<8;++i) Work(i);
    printf("%lld\n%lld\n",Ans1,Ans2);
}

T2

90pts:直接枚举全排列,爆搜然后判断

100pts:

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!