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.
    """
    def qtd(sentence, query):
        """
        calculates the query term density.
        That is, proportion of terms in sentence that are also in query.
        """
        query = set(query)
        count = sum([1 if word in query else 0 for word in sentence.split()])
        return count / len(sentence.split())

    ranked = {}
    for sentence, wordlist in sentences.items():
        ranked[sentence] = 0
        for word in query:
            if word in wordlist:
                ranked[sentence] += idfs.get(word, 0)

    ranked = dict(sorted(ranked.items(), key=lambda x: x[1], reverse=True))
    ranked = list(ranked.items())[:n]

    # Tie breaker using query term density
    for index in range(n - 1):
        if ranked[index][1] == ranked[index+1][1]:
            left = qtd(ranked[index][0], query)
            right = qtd(ranked[index][0], query)
            if right > left:
                ranked[index], ranked[index + 1] = ranked[index + 1], ranked[index]

    ranked = [item[0] for item in ranked]
    return ranked