均分紙牌及糖果傳遞問題

題目鏈接:

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 的中位數即可。

代碼:

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章