To solve this optimization problem, we need to maximize viewership while adhering to the given constraints. Let's denote the number of ads placed in airports, malls, and movie theatres as A, M, and T respectively.

The objective function is to maximize total viewership: 100,000A + 40,000M + 10,000T.

Constraints are:
1. Budget constraint: 10,000A + 3,000M + 2,000T ≤ 100,000.
2. Airport limit: A ≤ 5.
3. Movie theatre proportion: T ≤ (A + M + T)/2.
4. Mall proportion: M ≥ 0.3(A + M + T).

We can translate these constraints and the objective function into Gurobi code in Python.

```python
from gurobipy import *

# Create a model
m = Model("Ad_Placement")

# Define variables
A = m.addVar(name='Airports', vtype=GRB.INTEGER, lb=0)
M = m.addVar(name='Malls', vtype=GRB.INTEGER, lb=0)
T = m.addVar(name='Movie_Theatres', vtype=GRB.INTEGER, lb=0)

# Set objective function
m.setObjective(100000*A + 40000*M + 10000*T, GRB.MAXIMIZE)

# Add constraints
m.addConstr(A <= 5, name='Airport Limit')
m.addConstr(10000*A + 3000*M + 2000*T <= 100000, name='Budget Constraint')
m.addConstr(T <= (A + M + T)/2, name='Movie Theatre Proportion')
m.addConstr(M >= 0.3*(A + M + T), name='Mall Proportion')

# Optimize model
m.optimize()

# Print results
if m.status == GRB.OPTIMAL:
    print('Optimal solution found:')
    print('Airports:', A.x)
    print('Malls:', M.x)
    print('Movie Theatres:', T.x)
else:
    print('No optimal solution found')
```