1. 程式人生 > >[貪心]NOIP2013-火柴排隊

[貪心]NOIP2013-火柴排隊

分析 getchar stream 歸並 結果 一個空格 %d 證明 數字

題目描述

涵涵有兩盒火柴,每盒裝有 n 根火柴,每根火柴都有一個高度。 現在將每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 兩列火柴之間的距離定義為: \(\sum_{i=1}^n{(ai-bi)^2}\)

其中 ai 表示第一列火柴中第 i 個火柴的高度,bi 表示第二列火柴中第 i 個火柴的高度。

每列火柴中相鄰兩根火柴的位置都可以交換,請你通過交換使得兩列火柴之間的距離最小。請問得到這個最小的距離,最少需要交換多少次?如果這個數字太大,請輸出這個最小交換次數對 99,999,997 取模的結果。

輸入輸出格式

輸入格式:

輸入文件為 match.in。

共三行,第一行包含一個整數 n,表示每盒中火柴的數目。

第二行有 n 個整數,每兩個整數之間用一個空格隔開,表示第一列火柴的高度。

第三行有 n 個整數,每兩個整數之間用一個空格隔開,表示第二列火柴的高度。

輸出格式:

輸出文件為 match.out。

輸出共一行,包含一個整數,表示最少交換次數對 99,999,997 取模的結果。

輸入輸出樣例

輸入樣例#1:
【輸入輸出樣例 1】
4
2 3 1 4
3 2 1 4
【輸入輸出樣例 2】
4
1 3 4 2
1 7 2 4
輸出樣例#1:
【輸入輸出樣例 1】
1
【輸入輸出樣例 2】
2
說明
【輸入輸出樣例說明1】

最小距離是 0,最少需要交換 1 次,比如:交換第 1 列的前 2 根火柴或者交換第 2 列的前 2 根火柴。

【輸入輸出樣例說明2】

最小距離是 10,最少需要交換 2 次,比如:交換第 1 列的中間 2 根火柴的位置,再交換第 2 列中後 2 根火柴的位置。

數據範圍

對於 10%的數據, 1 ≤ n ≤ 10;

對於 30%的數據,1 ≤ n ≤ 100;

對於 60%的數據,1 ≤ n ≤ 1,000;

對於 100%的數據,1 ≤ n ≤ 100,000,0 ≤火柴高度≤ maxlongint

問題分析

把式子拆開,我們發現要求的就是最大的\(\sum_{i=1}^n{ai*bi}\)

猜一發結論是排序之後所得答案最優

反證法證明:
\(a>b\),\(c>d\),且\(ad+bc>ac+bd\)
\(a*(c-d)<b(c-d)*\)

又因為\(c-d>0\)
所以\(a<b\),與\(a>b\)矛盾,所以原命題得證

實現方法,把a數組中的數映射為下標,在b數組裏找逆序對即可

記得取模!忘記取模80分調了半天

#include<cstdio>
#include<cstring>
#include<iostream>
#define lowbit(x) ((x)&-(x))
const int maxn=100000+5;
const int mod=99999997;
inline int Read(){
    int x=0;char c=getchar();
    while(‘0‘>c||c>‘9‘)c=getchar();
    while(‘0‘<=c&&c<=‘9‘)x=(x<<1)+(x<<3)+c-48,c=getchar();
    return x;
}
int n,ans,ha[maxn],b[maxn],c[maxn];
inline void Add(int x){
    while(x)c[x]++,x-=lowbit(x);
}
inline int Getsum(int x){
    int re=0;
    while(x<=n){
        re+=c[x];
        x+=lowbit(x);
    }
    return re;
}
int main(){
    freopen("match.in","r",stdin);
    n=Read();
    for(int i=1;i<=n;++i)ha[Read()]=i;
    for(int i=1;i<=n;++i)b[i]=ha[Read()];
    for(int i=1;i<=n;++i){
        ans=(ans+Getsum(b[i]+1))%mod;
        Add(b[i]);
    }
    printf("%d\n",ans);
    return 0;
}

歸並排序

#include<cstdio>
#include<cstring>
#include<iostream>
const int maxn=100000+5;
const int mod=99999997;
inline int Read(){
    int x=0;char c=getchar();
    while(‘0‘>c||c>‘9‘)c=getchar();
    while(‘0‘<=c&&c<=‘9‘)x=(x<<1)+(x<<3)+c-48,c=getchar();
    return x;
}
int n,ans,ha[maxn],b[maxn],c[maxn];
void Merge(int l,int mid,int r){
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r){
        if(b[i]<=b[j])c[k++]=b[i++];
        else{
            ans=(ans+mid-i+1)%mod;
            c[k++]=b[j++];
        }
    }
    while(i<=mid)c[k++]=b[i++];
    while(j<=r)c[k++]=b[j++];
    for(int i=l;i<=r;++i)b[i]=c[i];
}
void Mergesort(int l,int r){
    if(l==r)return;
    int mid=(l+r)>>1;
    Mergesort(l,mid);Mergesort(mid+1,r);
    Merge(l,mid,r);
}
int main(){
    n=Read();
    for(int i=1;i<=n;++i)ha[Read()]=i;
    for(int i=1;i<=n;++i)b[i]=ha[Read()];
    Mergesort(1,n);
    printf("%d\n",ans);
    return 0;
}

[貪心]NOIP2013-火柴排隊