"const int a" is a constant integer. It cannot be modified. If you try to assign to it, the compiler won't let you. If you successfully play tricks to get around the compiler, your program might crash or worse.
"int* ptr" is a pointer to non-constant integers. If you write an assignment "*ptr = 100;" the compiler will allow it without any problems. If ptr is a null pointer or undefined, this will crash or worse. If ptr points to non-constant integer, it will assign 100 to that integer. If ptr points to a constant integer, it will crash or worse.
Because of that, the compiler doesn't allow you to assign the address of a to p. Because it is asking for trouble.
Now the assignment "ptr = (int *)&a;". &a is the address of a. a is a constant integer, &a is a pointer to a constant integer. (int *) is a cast. You tell the compiler to convert &a to a "pointer to int". Because it is a pointer to int, not pointer to const int, you can assign this to ptr. But the thing that ptr points to is still a and it is still constant.
If you read *ptr, this will work just fine. Reading *ptr will give the number 10. Writing to *ptr, like *ptr = 100, will crash or worse. Basically, by using the cast you told the compiler "shut up, I know what I'm doing". You better do.