均分纸牌及糖果传递问题

7 篇文章 0 订阅
4 篇文章 0 订阅

题目链接:

http://codevs.cn/problem/1098/

题意:

有 N 堆纸牌,编号分别为 1,2,…, N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若于张纸牌,然后移动。
移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。
现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

分析:

首先明确顺序随意且纸牌的移动顺序并不影响最终的移动次数。
那么我们假设按照由左而右的顺序移动。
首先考虑第一张纸牌,如果数量不满足平均,那么后续必有一个步骤是移走他多余的纸牌的,所以肯定是把他所有多余纸牌一次性拿走是最优的,同理若不足平均值,则用他右边的纸牌数来一次性补充满足,那么现在问题来了,如果右边的纸牌数不够移给他左边的怎么办。。我们在移动过程中,只是改变了移动的顺序,而移动的次数不变,后面肯定会有一个步骤使得右边的纸牌数增加,那么我们让这个步骤先行即可。
贪心的关键是,减少操作数一次性让纸牌满足条件,顺序不会影响答案,出现负数的情况也继续操作。

代码:

/*
On a hill is a tree, on a tree is a bough;
My heart for the Lord, but he never knows.

--Created by jiangyuzhu
--2016/5/17
*/
#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<stack>
#include<vector>
#include<algorithm>
#include<map>
#include<set>
#include<cmath>
using namespace std;
#define pr(x) cout << #x << ": " << x << "  "
#define pl(x) cout << #x << ": " << x << endl;
#define sa(x) scanf("%d",&(x))
#define sal(x) scanf("%I64d",&(x))
#define mdzz cout<<"mdzz"<<endl;
typedef long long ll;
const int maxn = 1e2 + 5, mod = 1e9 + 7;
int a[maxn];
int main(void)
{
   int n;sa(n);
   int tot = 0;
   for(int i = 0; i < n; i++){
        sa(a[i]);
        tot += a[i];
   }
    tot /= n;
    int ans = 0;
    for(int i = 0; i < n; i++){
        if(a[i] == tot) continue;
        if(a[i] < tot) a[i + 1] -= tot - a[i];
        if(a[i] > tot) a[i + 1] += a[i] - tot;
        ans++;
    }
    printf("%d\n", ans);
    return 0;
}

题目链接:

http://www.lydsy.com/JudgeOnline/problem.php?id=1045

题意:

n 个小朋友坐成一圈,每人有ai个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为1。问最小代价。

分析:

证明摘自:http://hzwer.com/2656.html
我们用 xi 表示第 i 个小朋友给第i1个小朋友的糖果数,其中 x1 表示第 1 个小朋友给第n个朋友的糖果数,那么最终答案即为 |x1|+|x2|+...|xn|
我们假设最后每个人剩 avg 个糖果,那么可以得到:
对于第一个小朋友: a1+x2x1=avg
对于第二个小朋友: a2+x3x2=avg

对于最后一个小朋友: an+x1xn=avg
整理一下即可得到:
x2=avga1+x1
x3=avga2+x2=2avg+x1a2a1

xn=avgan1+xn1=(n1)avg+x1n1i=1ai
我们令 ci=ij=1ai+i×avg ,上述式子即可转化为求解 |x1|+|x1c1|+|x1c2|...+|x1cn1| 的最小值,那么直接令 x1 等于 c <script type="math/tex" id="MathJax-Element-526">c</script>的中位数即可。

代码:

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<stack>
#include<vector>
#include<algorithm>
#include<map>
#include<set>
#include<cmath>
using namespace std;
#define pr(x) cout << #x << ": " << x << "  "
#define pl(x) cout << #x << ": " << x << endl;
#define sa(x) scanf("%d",&(x))
#define sal(x) scanf("%lld",&(x))
#define mdzz cout<<"mdzz"<<endl;
typedef long long ll;
const int maxn = 1e6 + 5, mod = 1e9 + 7;
ll a[maxn], c[maxn];
int main(void)
{
   int n;sa(n);
   ll tot = 0;
   for(int i = 0; i < n; i++){
        sal(a[i]);
        tot += a[i];
   }
    tot /= n;
    ll ans = 0;
    for(int i = 0; i < n; i++)
        c[i] = c[i - 1] + a[i] - tot;
    sort(c, c + n);
    int t = c[n / 2];
    for(int i = 0; i < n; i++){
        ans += abs(c[i] - t);
    }
    printf("%lld", ans);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值