1. 程式人生 > >FZU 1919 -- K-way Merging sort(記憶化搜索)

FZU 1919 -- K-way Merging sort(記憶化搜索)

one initial map from max task sys fzu pointer

題目鏈接

Problem Description

As we all known, merge sort is an O(nlogn) comparison-based sorting algorithm. The merge sort achieves its good runtime by a divide-and-conquer strategy, namely, that of halving the list being sorted: the front and back halves of the list are recursively sorted separately, then the two results are merged into the answer list. An implementation is shown as follows:
技術分享

The procedure Merge(L1,L2:in List_type;L:out List_type) that we have in mind for sorting two lists is described as follows. Initialize pointers to the first item in each list L1,L2, and then

repeat

compare the two items pointed at;

move the smaller into L;

move the pointer which originally points at the smaller one to the next number;

until one of L1,L2 exhausts;

drain the remainder of the unexhausted list into L;

Now let us come to the situation when there are k pointers, here k≥2. Let L be a list of n elements. Divide L into k disjoint contiguous sublists L1,L2,…,Lk of nearly equal length. Some Li’s (namely, n reminder k of them, so possibly none) will have length 技術分享

, let these have the low indices: L1,L2,…,Ln%k Other Li’s will have length 技術分享, and high indices are assigned: Ln%k+1,…,Lk-1,Lk. We intend to recursively sort the Li’s and merge the k results into an answer list.

We use Linear-Search-Merge here to merge k sorted lists. We find the smallest of k items (one from each of the k sorted source lists), at a cost of k-1 comparisons. Move the smallest into the answer list and advances its corresponding pointer (the next smallest element) in the source list from which it came. Again there are k items, from among which the smallest is to be selected. (When i (1 ≤ i < k) lists are empty, k-way merging sort becomes to (k-i)-way merging sort, and the draining process will start when the total order of all the elements have been found)

Given a list containing n elements, your task is to find out the maximum number of comparisons in k-way merging sort.

技術分享 Input

The first line of the input contains an integer T (T <= 100), indicating the number of cases. Each case begins with a line containing two integer n (1 ≤ n ≤ 10100) and k (2 ≤ k ≤ 20), the number of elements in the list, and it is k-way merging sort.

技術分享 Output

For each test case, print a line containing the test case number (beginning with 1) and the maximum number of comparisons in k-way merging sort.

技術分享 Sample Input

4 2 2 3 2 100 7 1000 10

技術分享 Sample Output

Case 1: 1 Case 2: 3 Case 3: 1085 Case 4: 22005 題意:對於歸並排序,本來是2-路分治,現在要求k-路分治,求n個元素下k-路分治歸並排序所需要的最大比較次數? 思路:對於n個元素k-路分治,將元素1~n分為k段,前s=n%k段每段有n/k+1個元素,後k-s段每段有n/k個元素,那麽對這k段進行歸並時和2-路一樣,選出這k段中的最小值放入合並後的數組,每次從k段中選出一個最小值需要比較k-1次,當每一段均剩下一個元素時,共選出了n-k個最小值,所以比較了(k-1)*(n-k)次,對於剩下的k個元素則需要比較(k-1)+(k-2)+……+1=(k-1)*k/2 次,所以共需要(k-1)*(n-k)+(k-1)*k/2次,然後遞歸進一步分治,註意使用記憶化搜索。 註意:本題數據很大所以不能用數組存值,而用的map映射的。 代碼如下:
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main{
    static Map<BigInteger,BigInteger>dp = new HashMap<BigInteger,BigInteger>();
    static BigInteger n, ans;
    static int k;
    static BigInteger dfs(BigInteger len, BigInteger x){
        if(dp.containsKey(len)) return x.multiply(dp.get(len));
        if(len.compareTo(BigInteger.valueOf(k))<=0){
            return x.multiply(len.subtract(BigInteger.ONE)).multiply(len).divide(BigInteger.valueOf(2));
        }
        BigInteger tmp = (BigInteger.valueOf(k).subtract(BigInteger.ONE)).multiply((len.subtract(BigInteger.valueOf(k))));
        tmp = tmp.add(BigInteger.valueOf(k).multiply(BigInteger.valueOf(k).subtract(BigInteger.ONE)).divide(BigInteger.valueOf(2)));
        BigInteger kk = len.mod(BigInteger.valueOf(k));
        if(kk!=BigInteger.ZERO){
            tmp=tmp.add(dfs(len.divide(BigInteger.valueOf(k)).add(BigInteger.ONE),kk));
        }
        tmp = tmp.add(dfs(len.divide(BigInteger.valueOf(k)),BigInteger.valueOf(k).subtract(kk)));
        dp.put(len, tmp);
        return tmp.multiply(x);
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int T = in.nextInt();
        for(int cas=1; cas<=T; cas++){
            dp.clear();
            n = in.nextBigInteger();
            k = in.nextInt();
            ans=dfs(n, BigInteger.ONE);
            System.out.println("Case "+cas+": "+ans);
        }
    }
} 

下面這個是我先用c++寫的版本,用來驗證算法,上面的java是用這c++代碼改寫的(其實一樣)。

#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <map>
using namespace std;
typedef long long LL;
LL n,k;
map<LL,LL>mp;

LL dfs(LL len,LL x)
{
    if(mp.count(len)) return x*mp[len];
    if(len<=k) return x*(len-1)*len/2;
    LL tmp=(k-1)*(len-k);
    tmp+=k*(k-1)/2;
    tmp+=dfs(len/k+(len%k!=0),(len%k));
    tmp+=dfs(len/k,(k-len%k));
    mp[len]=tmp;
    return tmp*x;
}

int main()
{
    while(scanf("%lld%lld",&n,&k)!=EOF)
    {
        mp.clear();
        LL ans=dfs(n,1);
        cout<<"final ans = "<<ans<<endl;
    }
    return 0;
}

FZU 1919 -- K-way Merging sort(記憶化搜索)