今天学了一个新的知识:
ACwing122:糖果传递
题解:
如图,a1给an的糖果数目设为x1,a2给a1的糖果数目设为x2.已知最后每个人得到的糖果数目一样设为(avg).那么可以知道对于a1来说有方程->a1-x1+x2=avg.对于a2来说有方程->a2-x2+x3=avg…以此类推.
我们可以把
a1-x1+x2=avg转化成x1-x2=a1-avg.
a2-x2+x3=avg转化为x2-x3=a2-zvg.
…
即
x1-x2=a1-avg.
x2-x3=a2-avg.
…
xn-2-xn-1=an-2-avg.
xn-1-xn=an-1-avg.(1) //这里表示为(1)式方便后面的查找
xn-x1=an-avg.
那么
xn=x1-(avg-an).(2)
xn-1=xn+an-1-avg(1)//由(1)式转化而来,上面也是如此,把(2)式带入xn=x1-(avg-an)+an-1+avg=x1-(2avg-an-an-1)).
易知:
要求的x1+x2+x3+…xn的值要最小.
那么即求
xn=|x1-(avg-an)|
xn-1=|x1-(2avg-an-an-1))|
…
x1=|x1-0|的值最小.
不难看出就是要让|x1-dis|值最小,易知此时若x1为所有dis值里面的中位数时|x1-dis|的值最小.
那么先求出所有的dis用一个数组c存储,再排序,其中c[(n+1)/2]就是中位数也就是x1的值.
不难看出dis的值符合递推式c[i]=c[i+1]+avg-a[i].
AC代码如下:
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a[1000005],c[1000005];
ll n,sum,avg;
ll func(){
avg=sum/n;
for(int i=n;i>1;i--){
c[i]=c[i+1]+avg-a[i];
}
c[1]=0;
ll ans=0;
sort(c+1,c+1+n);
for(int i=1;i<=n;i++){
ans+=abs(c[(n+1)/2]-c[i]);
}
return ans;
}
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){scanf("%lld",&a[i]);
sum+=a[i];
}
printf("%lld\n",func());
}
七夕祭ACwing105.
不难看出这道题和上面的糖果传递有异曲同工之妙
例如当n=3,m=2,k=2时,如上图所示,因为k%n!=0那么就说明不能让每行都有一样数目的摊位,k%m==0那么说明可以让每列有相同的摊位.就变成了糖果传递的问题了.
AC代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
#define ll long long
int row[N],col[N];
int c[N];
int k;
ll func(int a[],int n){
memset(c,0,sizeof(c));
ll avg=k/n;
for(int i=n;i>1;i--){
c[i]=c[i+1]+avg-a[i];
}
c[1]=0;
sort(c+1,c+1+n);
ll ans=0;
for(int i=1;i<=n;i++){
ans+=abs(c[(n+1)/2]-c[i]);
}
return ans;
}
int main(){
int n,m;
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<k;i++){
int t1,t2;
scanf("%d%d",&t1,&t2);
row[t1]++;
col[t2]++;
}
if(k%n==0&&k%m==0){
printf("both %lld\n",func(row,n)+func(col,m));
}
else if(k%n==0){
printf("row %lld\n",func(row,n));
}
else if(k%m==0)printf("column %lld\n",func(col,m));
else printf("impossible\n");
}