本文使用Typora编辑,这或许是windows平台上最好用的本地markdown编辑器了
我又来博客划水啦
啊我起标题都好随意啊
今天讲的内容很水,就是贪心。
可能很多人会觉得贪心都是错的,其实有些简单的问题,贪心就是正解了!(动态规划好烦
今天的混更分量非常大!
先引入一题真题。
这题的思路就是贪。一堆纸牌,总能也仅能分给两边的纸牌堆或从两边的纸牌堆得到纸牌。
记总n堆中第i堆纸牌数量为a[i],总和为sum
计算出均分后每堆纸牌数量sum/n
a[i]=a[i]-sum/n,就是每堆的操作纸牌数。
- >0,则是多了,需要移到右边(为了方便,都从左边移到右边),a[i+1]+=a[i]
- =0,则正好足够,不需要移动
- <0,则不够,需要从右边移过来。a[i+1]+=a[i](a[i]已经是负数了)
实现起来的话,就是这样的:
#include<iostream>
#include<cstdio>
using namespace std;
inline void in(int &p,char c=getchar())
{
while(c<'0' or c>'9')
c=getchar();
p=0;
while(c>='0' and c<='9')
p=p*10+c-'0',c=getchar();
}
int a[101],aver,ans;
int main()
{
int n;
in(n);
for(int i=0;i<n;i++)
{
in(a[i]);
aver+=a[i];
}
aver/=n;
for(int i=0;i<n-1;i++)
{
a[i]-=aver;
if(a[i])
a[i+1]+=a[i],ans++;
}
cout<<ans;
return 0;
}
好,下面升级一下。
第1堆和第n堆之间也可以给牌了。
也就是,原来链状的纸牌堆,变成了环状。
这个时候如果还用上面的方法不改动的话就会算多次数(所以说可以枚举起点,算n次再取min)
对于环状,正解是找一个最优的..额
设 bi 的前缀和为 si。如果从第 k 个位置开始,那么第 i 堆和第 i+1 堆交换的纸牌数就是 |si-sk|。总代价就是|s1-sk|+|s2-sk|+|s3-sk|+……+|sn-sk|。发现什么了?当 sk 是 s1~sn 中位数的时候,上式有最小值!所以把 si 排序后,令 sk=s[(n+1)/2],计算代价即可。
时间复杂度 O(nlogn),预计得分 100 分。
实现起来的话,是这样的
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int in(int &p,char c=getchar())
{
while(c<'0' or c>'9')
c=getchar();
p=0;
while(c>='0' and c<='9')
p=p*10+c-'0',c=getchar();
return p;
}
inline long long inl(long long &p,char c=getchar())
{
while(c<'0' or c>'9')
c=getchar();
p=0;
while(c>='0' and c<='9')
p=p*10+c-'0',c=getchar();
return p;
}
long long hang[1000000+1],f[1000000+1];
int n,m,t,x;
long long calc_hang()
{
long long ans=0;
hang[0]/=n;
for(int i=1;i<=n;i++)
{
hang[i]-=hang[0];
f[i]=f[i-1]+hang[i];
}
sort(f+1,f+n+1);
for(int i=1;i<=n;i++)
ans+=abs(f[i]-f[(1+n)>>1]);
return ans;
}
int main()
{
in(n);
for(int i=1;i<=n;i++)
hang[0]+=inl(hang[i]);
printf("%lld",calc_hang());
return 0;
}
好像也很好懂(似懂非懂.jpg)呢!
最后再来个变式
如果您打不开这个题目,说明您和这题没有缘分,请点右上角关闭
这题又有行,又有列的,吓死人了。
其实只是多了一大堆纸牌。
先计算以行为单位,各行的摊位数,sum摊位数
再计算以列为单位,各列的摊位数,sum摊位数
如果连%行数 or %列数都不==0,说明无论怎么分,都分不均匀的。
再以行为单位考虑移动摊位,以列为单位考虑移动摊位。
最后简单相加即得答案。
if(hang[0]%n==0 and lie[0]%m==0)
printf("both %lld",calc_hang()+calc_lie());
else if(hang[0]%n==0)
printf("row %lld",calc_hang());
else if(lie[0]%m==0)
printf("column %lld",calc_lie());
else
printf("impossible");
我这里为了节约空间,把sum存在0了。
最后的最后,%一下轻松ac的だらお们