It's the Climb

Ain't no mountain high enough

【贪心】纸牌均分 + 堆泥堆

均分纸牌问题的分析:

均分纸牌问题:有 N 堆纸牌,编号分别为 1,2,…, N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若于张纸牌,然后移动。

  移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

  现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

  例如 N=4,4 堆纸牌数分别为:

  ① 9 ② 8 ③ 17 ④ 6

  移动3次可达到目的:

  从 ③ 取 4 张牌放到 ④ (9 8 13 10) -> 从 ③ 取 3 张牌放到 ②(9 11 10 10)-> 从 ② 取 1 张牌放到①(10 10 10 10)。

 先放代码在分析吧,代码比较短。(转自https://www.cnblogs.com/yuchenlin/p/4382027.html)

int main(){
    int N;
    int pokers[MAX];
    cin>>N;
    int total = 0;
    for (int i=0; i<N; i++) {
        cin>>pokers[i];
        total+=pokers[i];
    }
    int avg = total/N,times=0;
    for(int i=0;i<N;i++){
        if(pokers[i]!=avg){
            pokers[i+1] -= avg - pokers[i]; //重要!
            times++;
        }
    }
    cout<<times<<endl; 
}

可以看到最核心的那个循环的思想是这样的:

从第一堆牌开始处理,如果第一堆牌整好是avg那么就放在一边不管了。

如果第一堆牌不是avg,那么就要把第二堆牌(合法的移动只有从2移到1,这也是这个算法的精髓之处)移动几张到第一堆,恰好使第一堆等于avg,从而只考虑第二堆开始到第N堆为止这些堆如何搞的子问题。然后依次递归下去。

这里的一个小技巧是认为牌数可以为负数,这样才能继续下去。综上,这个步骤是合理的。但是看不出来是最优的。可见,贪心法确实是比较容易实现,因为比较符合人类直觉,但是不好证明。

再反过来看一下前面提到的几点,可行性满足,不可取消,每一次操作都是直接赋值,局部最优,当前情况下,只能从右往左移动,且贪心地想尽快让第一堆满足约束。




#include<iostream>
#include<bits/stdc++.h>
using namespace std;
long long a[100005];
long long b[100005];

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		long long n ;
		cin>>n;
		for(int i=1;i<=n;i++)
			scanf("%lld",&a[i]);
		for(int i=1;i<=n;i++)
			scanf("%lld",&b[i]);
		long long sum=0;
		for(int i=1;i<=n-1;i++)
		{
			if(a[i]!=b[i])
			{
				a[i+1]-=(b[i]-a[i]); //注意b[i]和a[i]相减顺序 
				sum+=abs(b[i]-a[i]);
			}
		}
		printf("%lld\n",sum);	
	}
	return 0;
	
}

关键语句

a[i+1]-=(b[i]-a[i]); //注意b[i]和a[i]相减顺序 

意思是,从第一堆开始到倒数第二堆:如果本堆少了,就从右边的那一堆里拿泥巴过来,否则就拿泥巴给右边那堆。(同样是注意认为泥堆可以为负数)。

就这样下去,到最后一堆一定是==b[i]即满足条件的。(不然这题无解)


这道题是埃森哲杯第十六届上海大学生程序设计大赛的A题,自己还是现去学习了纸牌均分问题才明白的.....还是菜了.....加油 !

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_38033475/article/details/79949081
个人分类: 蓝桥杯
上一篇【签到题】吃苹果
下一篇【spfa】成仙之路
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭