P1031 均分纸牌
这道题我想的方法很麻烦。
是一个模拟。
模拟的操作:遍历一遍数组,从左到右遇到大于平均值往后分摊,中途遇到大于平均数的数直接都分到这里。
遇到小于平均值的,往后叠小于平均值的数,遇到大于的数就分一点过去,直到比平均值差的值都被分摊完。
这样是线性的,主要操作是第二步,负的叠起来加上去等价于反向传回来,如果提前遇到大于平均数的就直接先往后分,
便于第二步的操作(这就是第一步)。
因为总体分的过程必然是从左到右和从右到左的过程,这样两步能够使这两个过程最小。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define FOR(i,n) for(register int i=1;i<=n;i++)
using namespace std;
int A[10050];
int main()
{
int N;
int sum = 0;
int ans = 0;
int cnt = 0;
int pre;
cin >> N;
FOR(i, N)
{
cin >> A[i];
sum += A[i];
}
sum /= N;
for(int i=1;i<=N;i++)
{
if(A[i]>sum)
{
pre=A[i]-sum;
A[i]=sum;
cnt=0;
for(int j=i+1;j<=N;j++)
{
++cnt;
if(A[j]>sum)
{
A[j]+=pre;
break;
}
else if(A[j]<sum)
{
if(sum-A[j]<=pre)
{
pre-=sum-A[j];
A[j]=sum;
}
else
{
A[j]+=pre;
pre=0;
}
}
if(pre==0){break;}
}
ans+=cnt;
}
else if(A[i]<sum)
{
pre=0;
pre-=sum-A[i];
A[i]=sum;
cnt=0;
for(int j=i+1;j<=N;j++)
{
cnt++;
if(A[j]<sum)
{
pre-=sum-A[j];
A[j]=sum;
}
else if(A[j]>sum)
{
if(A[j]-sum+pre>=0)
{
A[j]+=pre;
break;
}
else
{
pre+=A[j]-sum;
A[j]=sum;
}
}
}
ans+=cnt;
}
/* for(int k=1;k<=N;k++)
cout<<A[k]<<" ";
cout<<endl;
cout<<ans<<endl;
*/
}
cout<<ans<<endl;
}
大佬题解
主题思路和我一样只不过就是把我的两步操作化成了简单的一步。
事实上我的第一步和第二步不可能重叠,所以可以一遍跑过去。
代码就会非常简单了。
#include<cstdio>
#include<iostream>
using namespace std;
int A[2000];
int main()
{
int p = 0, cnt = 0;
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> A[i];
p += A[i];
}
p /= n;
for (int i = 1; i <= n; i++)
{
A[i] -= p;
}
for (int i = 1; i <= n; i++)
{
if (A[i] == 0)continue;
else
{
A[i + 1] += A[i];
cnt++;
}
}
cout << cnt;
}