I would suggest looking at various optimisations used by Deep Mind when they originally developed DQN. You can check their "rainbow" paper for ideas that improved performance.
In addition, I think you are missing the idea of frame stacking, which should help the agent better understand velocity which is currently not in your state representation, although the position of the head will often be a clue. To frame stack, change the representation to include the last few turns (3 turns total might be enough for you) and concatenate the channels. You could also take the opportunity to flatten the representation of a single frame*, since each grid point only ever contains a single entity (e.g. you could use 0 for empty, 1 for apple, -1 for snake body and -0.5 for snake head). That should reduce the number of parameters required for the estimator, which in turn may improve model performance.
Reward values like +100 might be a problem in that they could cause large gradients in a neural network whilst learning them. It is worth checking to see what happens when you normalise your reward values to just +1 for eating an apple. Termination of an episode when the snake crashes into itself, and largish discount factor, $\gamma = 0.99$ should be enough to discourage the snake from hitting itself or walls.
* In DQN they turned the game inputs into greyscale, but if you do that with your current colour choices, all the entities would have the same value. However using greyscale could work for you as well, if you had all the entities display with different colour intensities.