Here's the formulation of the linear program and the Gurobi code to solve it:

**Decision Variables:**

* `x1`: Number of ads placed in airports
* `x2`: Number of ads placed in malls
* `x3`: Number of ads placed in movie theatres

**Objective Function:**

Maximize total viewership:

`Maximize: 100000*x1 + 40000*x2 + 10000*x3`

**Constraints:**

* **Budget Constraint:** `10000*x1 + 3000*x2 + 2000*x3 <= 100000`
* **Airport Limit:** `x1 <= 5`
* **Movie Theatre Proportion:** `x3 <= 0.5*(x1 + x2 + x3)`
* **Mall Proportion:** `x2 >= 0.3*(x1 + x2 + x3)`
* **Non-negativity:** `x1, x2, x3 >= 0`
* **Integer Constraint:** `x1, x2, x3` are integers (since you can't place fractional ads)


```python
import gurobipy as gp
from gurobipy import GRB

# Create a new model
model = gp.Model("Ad_Placement")

# Create variables
x1 = model.addVar(vtype=GRB.INTEGER, name="airport_ads")
x2 = model.addVar(vtype=GRB.INTEGER, name="mall_ads")
x3 = model.addVar(vtype=GRB.INTEGER, name="theatre_ads")

# Set objective function
model.setObjective(100000*x1 + 40000*x2 + 10000*x3, GRB.MAXIMIZE)

# Add constraints
model.addConstr(10000*x1 + 3000*x2 + 2000*x3 <= 100000, "budget")
model.addConstr(x1 <= 5, "airport_limit")
model.addConstr(x3 <= 0.5*(x1 + x2 + x3), "theatre_proportion")
model.addConstr(x2 >= 0.3*(x1 + x2 + x3), "mall_proportion")

# Optimize model
model.optimize()

# Print results
if model.status == GRB.OPTIMAL:
    print(f"Optimal Viewership: {model.objVal}")
    print(f"Airport Ads: {x1.x}")
    print(f"Mall Ads: {x2.x}")
    print(f"Theatre Ads: {x3.x}")
elif model.status == GRB.INFEASIBLE:
    print("The model is infeasible.")
else:
    print(f"Optimization ended with status {model.status}")

```
