1. 程式人生 > >當七夕遇上算法競賽

當七夕遇上算法競賽

競賽 mes 農歷 include col scanf 經典 img 權限

2018/8/17  農歷 七月初七

今天這個特殊的日子,不知道各位算友都過得怎麽樣~

適逢七夕,寫算法題當然是眾汪所歸啊!所以今天,我們帶來了一道以七夕為背景的題目。

技術分享圖片

題目:七夕祭(BZOJ3032有權限,可在JoyOI和CodeVS上查找提交)

題目背景:

  七夕節因牛郎織女的傳說而被扣上了「情人節」的帽子。於是TYVJ今年舉辦了一次線下七夕祭。Vani同學今年成功邀請到了cl同學陪他來共度七夕,於是他們決定去TYVJ七夕祭遊玩。

題目描述:

  TYVJ七夕祭和11區的夏祭的形式很像。矩形的祭典會場由N排M列共計N×M個攤點組成。雖然攤點種類繁多,不過cl只對其中的一部分攤點感興趣,比如章魚燒、蘋果糖、棉花糖、射的屋……什麽的。Vani預先聯系了七夕祭的負責人zhq,希望能夠通過恰當地布置會場,使得各行中cl感興趣的攤點數一樣多,並且各列中cl感興趣的攤點數也一樣多。
  不過zhq告訴Vani,攤點已經隨意布置完畢了,如果想滿足cl的要求,唯一的調整方式就是交換兩個相鄰的攤點。兩個攤點相鄰,當且僅當他們處在同一行或者同一列的相鄰位置上。由於zhq率領的TYVJ開發小組成功地扭曲了空間,每一行或每一列的第一個位置和最後一個位置也算作相鄰。現在Vani想知道他的兩個要求最多能滿足多少個。在此前提下,至少需要交換多少次攤點。

輸入格式:

  第一行包含三個整數N和M和T。T表示cl對多少個攤點感興趣。
  接下來T行,每行兩個整數x, y,表示cl對處在第x行第y列的攤點感興趣。

輸出格式:

  首先輸出一個字符串。如果能滿足Vani的全部兩個要求,輸出both;如果通過調整只能使得各行中cl感興趣的攤點數一樣多,輸出row;如果只能使各列中cl感興趣的攤點數一樣多,輸出column;如果均不能滿足,輸出impossible。
  如果輸出的字符串不是impossible, 接下來輸出最小交換次數,與字符串之間用一個空格隔開。

提示:

  對於30% 的數據,N, M≤100。
  對於70% 的數據,N, M≤1000。
  對於100% 的數據,1≤N, M≤100000,0≤T≤min(NM, 100000),1≤x≤N,1≤y≤M。

輸入樣例:

樣例輸入1
2 3 4
1 3
2 1
2 2
2 3

樣例輸入2
3 3 3
1 3
2 2
2 3

輸出樣例:

樣例輸出1
row 1

樣例輸出2
both 2

提示:環形均分紙牌、中位數的性質。

思路:

首先我們只要想一想就會發現只要用 t 去模 n和m 就能根據是否整除得到字符串輸出是哪個。而且整除的結果就是最後移動後的均攤結果。

然後就可以發現這是個橫向和縱向的均分紙牌有木有?(均分紙牌是noip2002的一道經典貪心題目,並不難,希望掌握以後再往下看……不懂記得問)

不加證明地給出行和列分別求就可以了不會影響,因為這一行的點互相交換並不會影響這一列的點的個數。

那我們以行為例:

在均分紙牌中,我們會用a[i] = c[i](實際上這行的數量)-average(均分後的結果)來便於操作,所以a[i]可能是正的也可能是負的,a[i]==0就說明這一行已經達成目標。

而 abs(a[i]) 就是要對這一行進行的操作數,通過前綴和s[i] = a[i] + s[i-1],我們可以通過 ∑mi=1 | s[i] | (m是行數)得到最後所需的總移動次數。

以上為均分紙牌的內容,並沒有詳細講。接下來只剩下一個問題了,就是這並不是原來的均分紙牌,此題由於可以畫環,所以並不能完全把前綴和 s[i] 加起來就解決了。

此處希望讀者在腦中、紙上自行想象如果第一行和最後一行相接會出現怎樣的、比常規更巧的分配方式。

然後當我們不知道怎麽處理之時神來之筆就是:一定存在一種最優解的方案,環上某兩個相鄰的人之間沒有發生紙牌交換操作。因此我們可以在這裏把環斷開,就當成從第k+1個人開始依次往下分,像原來一樣均分紙牌,一直分到第k個人結束。

普通的均分紙牌是這樣的:

技術分享圖片

我們從第k+1個開始的均分紙牌是這樣的:

技術分享圖片

所以由於s[m]肯定是等於0的,我們所需結果從技術分享圖片變成了技術分享圖片

接下來的問題是如果遍歷k的話復雜度撐不住。

巧求k,化公式為問題:坐標軸上有m個點,怎樣取一個第k點,使得各點到此點的距離之和最短。

結論:1~m個點的坐標排序後的中位數第k個點即是所需點。

簡略說明:假如k點左邊現在有P個點,右邊現在有Q個點,當k點在坐標軸上向右移動一個單位時,左邊每個點都遠離它一個單位,右邊每個點都接近它一個單位,總距離比剛才減少了Q-P個單位;這個過程繼續下去,當k為中位點是P=Q,沒法再少了,再往右走就過分了,同理得往左走了。

所以回到問題,可以把 s[i] 排序後直接取中位點即可。

至此所有的問題都解決了,如果都理解了的話可以自己動手解決了,如還有沒理解可以結合代碼看看、搜搜資料、後臺留言……最大復雜度(nlogn+mlogm)

 1 #include <bits/stdc++.h>
 2 #define maxn 100005
 3 using namespace std;
 4 
 5 int n, m, t, flag;
 6 long long ans;
 7 int rows[maxn], columns[maxn];
 8 
 9 void solve(int *a, int c)
10 {
11     int average = t / c;
12     int s[maxn] = {0};
13 
14     for (int i = 1; i <= c; i++)
15         a[i] -= average, s[i] = a[i] + s[i-1];
16     sort (s+1, s+1+c);
17     for (int i = 1; i <= c; i++)
18         ans += abs(s[i] - s[(c+1)>>1]);
19 }
20 
21 int main()
22 {
23     scanf("%d%d%d", &n, &m, &t);
24     for (int i = 0; i < t; ++i)
25     {
26         int x, y;
27         scanf("%d%d", &x, &y);
28         rows[x]++;
29         columns[y]++;
30     }
31 
32     if (t % n == 0)
33     {
34         flag++;
35         solve(rows, n);
36     }
37     if (t % m == 0)
38     {
39         flag += 2;
40         solve(columns, m);
41     }
42 
43     if (flag == 0)    printf("impossible");
44     else if (flag == 1)    printf("row ");
45     else if (flag == 2)    printf("column ");
46     else if (flag == 3)    printf("both ");
47 
48     if (flag)    printf("%lld", ans);
49 }

當七夕遇上算法競賽