def top_sentences(query, sentences, idfs, n):
    """
    Given a `query` (a set of words), `sentences` (a dictionary mapping
    sentences to a list of their words), and `idfs` (a dictionary mapping words
    to their IDF values), return a list of the `n` top sentences that match
    the query, ranked according to idf. If there are ties, preference should
    be given to sentences that have a higher query term density.
    """
    # Calculate matching word measures and query term densities
    value_list = []
    for id, tokens in enumerate(sentences.values()):
        mwm = 0
        words_match = 0
        sent_len = len(tokens)
        for word in query:
            if word in tokens:
                mwm += idfs[word]
                words_match += 1 / sent_len  # Track number of matched words
        value_list.append((mwm, words_match, id))  # Use sort's tuple ordering to break ties

    sorted_list = sorted(value_list)
    sorted_list = sorted_list[::-1]
    sentence_list = list(sentences.keys())  # Get sentences
    ordered_sentences = [sentence_list[index[2]] for index in sorted_list]  # Sort sentences
    return ordered_sentences[:n]