Di base, l'idea sarebbe di definire due costanti che delimitino la gamma dei voti possibili, ad esempio...
#define VOTO_MIN 0
#define VOTO_MAX 10
A questo punto, si può definire un vettore di interi in grado di contenere l'intera gamma dei voti, minimo e massimo compresi. Avendo dimensioni fisse, non serve scomodare l'allocazione dinamica della memoria dinamica. Il vettore viene inizializzato in modo da contenere tutti zeri.
int eQV[VOTO_MAX-VOTO_MIN+1] = {0};
(eQV = "elenco quantità voti")
Se scorri uno per uno l'elenco dei laureandi e, in un ciclo annidato, l'elenco degli esami sostenui da ogni candidato, puoi incrementare l'elemento di eQV che corrisponde al voto assegnato al laureando. Ad esempio, se il primo laureando ha preso un 6, incrementi eQV[6]; se ha preso un 8, incrementi eQV[8]. E' facile cogliere che alla fine dei cicli, eQV[0] conterrà la quantità degli zeri assegnati, eQV[1] conterrà la quantità degli uno, eQV[2] la quantità dei due... e, da ultimo, eQV[10] conterrà la quantità dei dieci assegnati.
Trovare il voto assegnato con maggiore frequenza significa semplicemente trovare il valore massimo nell'ambito del vettore eQV.
Non ti mostro il codice completo perché non voglio suscitare vespai, però è veramente semplice.
P.S. Se dovesse venir fuori che devi "scorporare" la frequenza di assegnazione dei voti in base alla materia, basterebbe definire una matrice bidimensionale anziché un vettore.