Why do top and ps show different PIDs for the same processes?

They are actually showing the same information in different ways. This is what the -f and -L options to ps do (from man ps, emphasis mine):

-f               Do full-format listing. This option can be combined with many other UNIX-style options to add additional columns. It also causes the command arguments to be printed. When used with -L, the NLWP (number of threads) and LWP (thread ID) columns will be added.

-L              Show threads, possibly with LWP and NLWP columns.

tid              TID the unique number representing a dispatacable entity (alias lwp, spid). This value may also appear as: a process ID (pid); a process group ID (pgrp); a session ID for the session leader (sid); a thread group ID for the thread group leader (tgid); and a tty process group ID for the process group leader (tpgid).

So, ps will show thread IDs in the LWP column while the PID column is the actual process identifier.

top on the other hand, lists the different threads in the PID column though I can't find an explicit mention of this in man top.

It's just a difference in presentation. Look at the LWP column on your ps output -- LWP is LightWeight Process. The kernel can distinguish between a full process and a thread, but it still has to schedule them independently (that being the purpose of a thread) with the same mechanism it uses to schedule processes and so the id's the scheduler sees have to be unique, best achieved by having a single pool of process and thread id's for every purpose and using other ways to tell the two kinds apart.