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

**Decision Variables:**

* `x1`: Number of ads in malls
* `x2`: Number of ads in bus stops
* `x3`: Number of ads in theatres

**Objective Function:**

Maximize total viewership: `50000*x1 + 10000*x2 + 20000*x3`

**Constraints:**

* **Budget Constraint:** `5000*x1 + 1000*x2 + 3000*x3 <= 30000`
* **Bus Stop Limit:** `x2 <= 20`
* **Theatre Proportion:** `x3 <= (1/3)*(x1 + x2 + x3)`  (or equivalently, `2*x3 <= x1 + x2`)
* **Mall Proportion:** `x1 >= 0.2*(x1 + x2 + x3)` (or equivalently, `4*x1 >= x2 + x3`)
* **Non-negativity:** `x1, x2, x3 >= 0`

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

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

# Create variables
x1 = model.addVar(lb=0, vtype=GRB.INTEGER, name="Mall Ads")
x2 = model.addVar(lb=0, vtype=GRB.INTEGER, name="Bus Stop Ads")
x3 = model.addVar(lb=0, vtype=GRB.INTEGER, name="Theatre Ads")

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

# Add constraints
model.addConstr(5000*x1 + 1000*x2 + 3000*x3 <= 30000, "Budget")
model.addConstr(x2 <= 20, "Bus Stop Limit")
model.addConstr(2*x3 <= x1 + x2, "Theatre Proportion")
model.addConstr(4*x1 >= x2 + x3, "Mall Proportion")


# Optimize model
model.optimize()

# Print results
if model.status == GRB.OPTIMAL:
    print(f"Optimal Viewership: {model.objVal}")
    print(f"Mall Ads: {x1.x}")
    print(f"Bus Stop 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}")

```
