This should possibly be the fastest option, but you should EXPLAIN ANALYZE all the answers to make sure.
SELECT *
FROM categories c
WHERE id IN (SELECT unnest(category_ids) from users where id=1)
SELECT *
FROM categories c
JOIN (SELECT unnest(category_ids) AS id from users where id=1) AS foo
ON c.id=foo.id
The first query will remove duplicates in the array due to IN(), the second will not.
I didn't test the syntax so there could be typos. Basically unnest() is a function that expands an array to a set of rows, ie it turns an array into a table that you can then use to join to other tables, or put into an IN() clause, etc. The opposite is array_agg() which is an aggregate function that returns an array of aggregated elements. These two are quite convenient.
If the optimizer turns the =ANY() query into a seq scan with a filter, then unnest() should be much faster as it should unnest the array and use nested loop index scan on categories.
You could also try this:
SELECT *
FROM categories c
WHERE id =ANY(SELECT category_ids from users where id=1)