我又来博客划水啦

本文使用Typora编辑,这或许是windows平台上最好用的本地markdown编辑器了

我又来博客划水啦

啊我起标题都好随意啊

今天讲的内容很水,就是贪心。

可能很多人会觉得贪心都是错的,其实有些简单的问题,贪心就是正解了!(动态规划好烦

今天的混更分量非常大!

先引入一题真题。

NOIp2002均分纸牌

这题的思路就是贪。一堆纸牌,总能也仅能分给两边的纸牌堆或从两边的纸牌堆得到纸牌。

记总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;
}

好,下面升级一下。

均分纸牌plus

第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)呢!

最后再来个变式

均分纸牌X

如果您打不开这个题目,说明您和这题没有缘分,请点右上角关闭

这题又有行,又有列的,吓死人了。

其实只是多了一大堆纸牌。

先计算以行为单位,各行的摊位数,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的だらお们

だらお

posted @ 2017-10-30 09:05  syhien  阅读(130)  评论(0编辑  收藏  举报