Let's start with the homing, because "keeping objects on the grid" and "keeping objects from running through stuff" is logic you'll need in multiple places.
Let's say the zombies are on the grid at location (x, y). And the nearest human object is at location (a, b). You can create a vector from (x, y) to (a, b) by subtracting (x, y) from (a, b). This vector is the amount you'd need to add to the zombies' location for them to be on top of the human.
For example, say the zombies are at (5, 8) and the human is at (12, 24). We subtract (5, 8) from (12, 24), getting (7, 16). And indeed, if you were to add (7, 16) to the zombies' location you'd be on top of the human.
Now of course, the zombies can't move that fast. Presumably they can only move one square at a time, and let's assume for now that they can't move diagonally. So take a look at that movement vector (7, 16). If they could only move in one direction, which direction would it be to get them closer to the human? They'd want to move downwards (assuming as always that Y distance increases as you go down, and X distance increases as you go right).
Of course, vectors can have negative components. If the human was to the left of the zombies, for example, then you might have a movement vector of (-8, 4). And even though -8 is smaller than 4, it's still the direction you want to move in. So you need to be careful to look at the magnitude of the movement vector, not just its amount.