Devise a backtracking algorithm to allocate $p$ projects to $s$ students
I didn't get what you're exatly asking for? Backtracking is the solution. Are you looking for an actual implementation or an explanation of backtracking? Or a  better  performing approach in terms of runtime.

Here my pseudo code how I would generate all matching solutions.

state = [];  //array representing the current state, assigns students to projects

S = [..];    //array containing the students
P = [..];    //array with students
func assign(level) {
  if(level == S.length) {
    if is_valid_state() //ensure each project has at least one student
      output(state);
  }
  for(i = 0; i &lt; P.length; i++) {
    state[level] = i;
    assign(level+1);
  }
}

level(0)


Can of course  be optimized by moving the final condition atop, but this would be my first thought of how to solving this the least efficient way (in terms of performance).