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:
来源:https://www.cnblogs.com/lcezych/p/12250142.html