1. 程式人生 > >分治算法小總結 x

分治算法小總結 x

include tdi 絕對值 spa none 限制 clas string 給定

分治算法的基本思想是將一個規模為 N 的問題分解為 K 個規模較小的子問題,這些子問題相互獨立且與原問題性質相同。求出子問題的解,就可得到原問題的解。

                  ——以上來自百度百科。

* 分治法解題的一般步驟:
1 分解,將要解決的問題劃分成若幹規模較小的同類問題;
- 二分法:區間對半分開
2 求解,當子問題劃分得足夠小時,用較簡單的方法解決;
- 邊界情況:可以直接操作
3 合並,按原問題的要求,將子問題的解逐層合並構成原問題的解。
- 合並操作:根據不同的題目來確定

其實這個題用冒泡排序做的,但用歸並排序也能做出來(分析一下此題與逆序對是有相同之處的)

由於兩者的代碼完全一樣,就只放一個啦 ~\(≧▽≦)/~啦啦啦

1.洛谷 P1116 車廂重組

題目描述

在一個舊式的火車站旁邊有一座橋,其橋面可以繞河中心的橋墩水平旋轉。一個車站的職工發現橋的長度最多能容納兩節車廂,如果將橋旋轉180度,則可以把相鄰兩節車廂的位置交換,用這種方法可以重新排列車廂的順序。於是他就負責用這座橋將進站的車廂按車廂號從小到大排列。他退休後,火車站決定將這一工作自動化,其中一項重要的工作是編一個程序,輸入初始的車廂順序,計算最少用多少步就能將車廂排序。

輸入輸出格式

輸入格式:

輸入文件有兩行數據,第一行是車廂總數N(不大於10000),第二行是N個不同的數表示初始的車廂順序。

輸出格式:

一個數據,是最少的旋轉次數。

輸入輸出樣例

輸入樣例#1:
4
4 3 2 1 
輸出樣例#1: 6
/*----------------------分割線---------------------*/
2.洛谷  P1908 逆序對

題目描述

貓貓TOM和小老鼠JERRY最近又較量上了,但是畢竟都是成年人,他們已經不喜歡再玩那種你追我趕的遊戲,現在他們喜歡玩統計。最近,TOM老貓查閱到一個人類稱之為“逆序對”的東西,這東西是這樣定義的:對於給定的一段正整數序列,逆序對就是序列中ai>aj且i<j的有序對。知道這概念後,他們就比賽誰先算出給定的一段正整數序列中逆序對的數目。

輸入輸出格式

輸入格式:

第一行,一個數n,表示序列中有n個數。

第二行n個數,表示給定的序列。

輸出格式:

給定序列中逆序對的數目。

輸入輸出樣例

輸入樣例#1:
6
5 4 2 6 3 1
輸出樣例#1:
11

說明

對於50%的數據,n≤2500

對於100%的數據,n≤40000。

思路:
歸並過程為:比較A[i]和A[j]的大小,若A[i]≤A[j],則將第一個有序表中的元素A[i]復制到R[k]中,並令i和k分別加1,即使之分別指問後一單元,否則將第二個有序表中的元素A[j]復制到R[k]中,並令j和k分別加1;如此循環下去,直到其中的一個有序表取完,然後再將另一個有序表中剩余的元素復制到R中從下標k到下標t的單元. 歸並排序算法我們用遞歸實現,先把待排序區間[s,t]以中點二分,接著把左邊子區間排序,再把右邊子區間排序,最後把左區間和右區間用一次歸並操作合並成有序的區間[s,t]。對左右子區間的排序與原問題一樣,所以我們可以調用同樣的子程序,只是區間大小不一樣。
代碼醬來也~
技術分享
#include<iostream>
#include<algorithm>
#define M 111111

using namespace std;

int n,ans;
int a[M],b[M];

void merge_sort(int l,int r)
{
    if(l == r) return;
    
    int m = (l+r) / 2 ;//中間值
    
    merge_sort(l,m);//左邊 
    merge_sort(m+1,r); //右邊
    
    int i=l,j=m+1,k=l;
    
    for(;i<=m&&j<=r;)
    {
        if(a[i]<=a[j]) b[k++]=a[i++];
        else 
        {
            ans+=m-i+1;//統計產生逆序對的個數
            b[k++]=a[j++]; 
        }
    }
    
    for(;i<=m;b[k++]=a[i++]);
    for(;j<=r;b[k++]=a[j++]);
    
    for(int i=l;i<=r;i++)
    a[i]=b[i];
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];

    merge_sort(1,n);

    cout<<ans;

    return 0;
}
View Code

/*----------------------分割線---------------------*/
 3.Codevs 1688 求逆序對
時間限制: 1 s 空間限制: 128000 KB 題目等級 : 黃金 Gold
題目描述 Description

給定一個序列a1,a2,…,an,如果存在i<j並且ai>aj,那麽我們稱之為逆序對,求逆序對的數目

數據範圍:N<=105。Ai<=105。時間限制為1s。

輸入描述 Input Description

第一行為n,表示序列長度,接下來的n行,第i+1行表示序列中的第i個數。

輸出描述 Output Description

所有逆序對總數.

樣例輸入 Sample Input

4

3

2

3

2

樣例輸出 Sample Output

3

數據範圍及提示 Data Size & Hint 思路:   這題有毒!!!where is the 數據範圍??? 這數據範圍太大了有沒有!!!所以int類型的就會炸,將int改為long long就ok了! 這題我交了
代碼醬來也~
技術分享
#include<iostream>
#include<algorithm>
#define M 111111
#define LL long long

using namespace std;

LL ans;
int n;
int a[M],b[M];

void merge_sort(int l,int r)
{
    if(l == r) return;
    
    int m = (l+r) / 2 ;//中間值
    
    merge_sort(l,m);//左邊 
    merge_sort(m+1,r); //右邊
    
    int i=l,j=m+1,k=l;
    
    for(;i<=m&&j<=r;)
    {
        if(a[i]<=a[j]) b[k++]=a[i++];
        else 
        {
            ans+=(LL)m-(LL)i+1; //統計產生逆序對的個數
            b[k++]=a[j++]; 
        }
    }
    
    for(;i<=m;b[k++]=a[i++]);
    for(;j<=r;b[k++]=a[j++]);
    
    for(int i=l;i<=r;i++)
    a[i]=b[i];
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];

    merge_sort(1,n);

    cout<<ans;

    return 0;
}
View Code

4.洛谷 P1115 最大子段和

題目描述

給出一段序列,選出其中連續且非空的一段使得這段和最大。

輸入輸出格式

輸入格式:

輸入文件maxsum1.in的第一行是一個正整數N,表示了序列的長度。

第2行包含N個絕對值不大於10000的整數A[i],描述了這段序列。

輸出格式:

輸入文件maxsum1.out僅包括1個整數,為最大的子段和是多少。子段的最小長度為1。

輸入輸出樣例

輸入樣例#1:
7
2 -4 3 -1 2 -4 3
輸出樣例#1:
4

說明

【樣例說明】2 -4 3 -1 2 -4 3

【數據規模與約定】

對於40%的數據,有N ≤ 2000。

對於100%的數據,有N ≤ 200000。

思路:

  這道題真的就是一道模板題,但是為什麽我交了好幾遍就是沒有AC呢?原因出在第二個點上,因為第二個點裏的數據似乎全部都是負的,所以在進行初始化的時候需要賦值為一個極小數(ans,lmax,rmax這三個),不然按一般的話,一定是從0開始,所以負數就出不來,所以……

代碼醬來也~

技術分享
#include <cstdio>
#include <iostream>
#include <algorithm>
#define LL long long 

using namespace std;

const int N = 2e5 + 6;

int n;
int a[N];

LL calc(int l, int r)
{
    if(l==r) return a[l];

    int m = (l+r) / 2;
    LL ret = max(calc(l, m), calc(m+1, r));

    int lmax = -1000000, rmax = -1000000;
    for(int i=m, s=0; i>=l; i--) s+=a[i], lmax=max(lmax, s);
    for(int i=m+1, s=0; i<=r; i++) s+=a[i], rmax=max(rmax, s);

    ret = max(ret, (LL)(lmax) + (LL)(rmax));
    return ret;
}

int main()
{
    scanf("%d", &n);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);

    LL ans=-1000000;
    ans = max(ans, (LL)calc(1, n));
    cout<<ans;

    return 0;
}
View Code

5.洛谷P1010 冪次方

題目描述

任何一個正整數都可以用2的冪次方表示。例如

    137=2^7+2^3+2^0         

同時約定方次用括號來表示,即a^b 可表示為a(b)。

由此可知,137可表示為:

    2(7)+2(3)+2(0)

進一步:7= 2^2+2+2^0 (2^1用2表示)

    3=2+2^0   

所以最後137可表示為:

    2(2(2)+2+2(0))+2(2+2(0))+2(0)

又如:

    1315=2^10 +2^8 +2^5 +2+1

所以1315最後可表示為:

    2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)

輸入輸出格式

輸入格式:

一個正整數n(n≤20000)。

輸出格式:

符合約定的n的0,2表示(在表示中不能有空格)

輸入輸出樣例

輸入樣例#1:
1315
輸出樣例#1:
2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)

思路:
  如果遇到2或者1,特判一下,直接輸出,其余的如果能夠繼續分解,就將其分解(分治~)
代碼:
技術分享
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>

using namespace std;

void work(int n)
{
    if(n==1)
    {
        printf("2(0)");
        return;
    }
    if(n==2)
    {
        printf("2");
        return;
    }
    else
    {
        int j=1,i=0;
        do
        {
            j*=2;
            if(j>n)///超出該數n 
            {
                j/=2;
                if(i==1) printf("2");///特判 
                else
                {
                    printf("2(");///輸出形式 
                    work(i);
                    printf(")");
                }
                if(n-j!=0)///若還能夠繼續分解 
                {
                    printf("+");///用+連接 
                    work(n-j);///繼續分解 
                }
                return;
            }
            else i++;///如果還不夠大,繼續加 
        }while(1);
    }
}

int main()
{
    int n;
    cin>>n;
    work(n);
    return 0;
}
View Code






分治算法小總結 x