1. 程式人生 > >基本算法- 七夕祭

基本算法- 七夕祭

code 線下 min n+1 至少 std class 描述 clas


題目描述

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

輸入

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

輸出

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

樣例輸入

2 3 4
1 3
2 1
2 2
2 3

樣例輸出

row 1
1

提示

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

分析

  • 首先不難發現列和行的操作是互不影響的,所以可以分開處理。
  • 我們要讓所有行變成一樣數量的話,這其實是一個“均分紙牌問題”,而且在本題中是環形的。
  • 設均分後每個人應有average張紙牌,把每個人現有紙牌數都減去average,那麽我們的均分目的及變成了每個人都有0張牌
  • 對於非環形的均分紙牌問題,最少移動數是前綴和的絕對值之和;那麽對於環形的來說我們可以枚舉前綴和開始算的起點,然後取所有答案的最小值。
  • 然後我們可以發現,以第一個元素為起點的各個前綴和減去pre[k]之後恰好是以第k+1個元素為起點的各個前綴和。
  • 問題轉化為減去哪一個pre[k]可以使得前綴和的絕對值最小,也就是\(\sum_{i=1}^{n}|pre[i]-pre[k]|\)最小
  • 顯然但pre[k]為前綴和數組中位數時最小

代碼

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=100050;
const ll MOD=1e9+7;
int a[maxn],b[maxn];
ll sum[maxn];
int main()
{
    int n,m,t;
    scanf("%d%d%d", &n,&m,&t);
    for(int i = 0; i < t; ++i){
        int x,y;
        scanf("%d%d", &x,&y);
        a[x]++;
        b[y]++;
    }
    if(t%n&&t%m){
        printf("impossible\n");
        return 0;
    }
    if(t%n==0&&t%m==0) printf("both ");
    else if(t%n==0) printf("row ");
    else printf("column ");

    ll s1=0,s2=0;
    if(t%n==0) {
        int p=t/n;
        for(int i = 1; i <= n; ++i){
            a[i]-=p;
            sum[i]=sum[i-1]+a[i];
        }
        sort(sum+1,sum+1+n);
        ll mid=sum[(n+1)/2];
        for(int i = 1; i <= n; ++i){
            s1+=abs(sum[i]-mid);
        }
    }

    if(t%m==0) {
        int p=t/m;
        for(int i = 1; i <= m; ++i){
            b[i]-=p;
            sum[i]=sum[i-1]+b[i];
        }
        sort(sum+1,sum+1+m);
        ll mid=sum[(m+1)/2];
        for(int i = 1; i <= m; ++i){
            s2+=abs(sum[i]-mid);
        }
    }
    printf("%lld\n", s1+s2);
    return 0;
}

基本算法- 七夕祭