```json
{
  "sym_variables": [
    ("x1", "number of mall ads"),
    ("x2", "number of bus stop ads"),
    ("x3", "number of theatre ads")
  ],
  "objective_function": "50000*x1 + 10000*x2 + 20000*x3",
  "constraints": [
    "5000*x1 + 1000*x2 + 3000*x3 <= 30000",
    "x2 <= 20",
    "x3 <= (x1 + x2 + x3)/3",
    "x1 >= 0.2*(x1 + x2 + x3)"
  ]
}
```

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

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

# Create variables
mall_ads = model.addVar(vtype=GRB.INTEGER, name="mall_ads")
bus_ads = model.addVar(vtype=GRB.INTEGER, name="bus_ads")
theatre_ads = model.addVar(vtype=GRB.INTEGER, name="theatre_ads")

# Set objective function
model.setObjective(50000 * mall_ads + 10000 * bus_ads + 20000 * theatre_ads, GRB.MAXIMIZE)

# Add constraints
model.addConstr(5000 * mall_ads + 1000 * bus_ads + 3000 * theatre_ads <= 30000, "budget")
model.addConstr(bus_ads <= 20, "bus_limit")
model.addConstr(theatre_ads <= (mall_ads + bus_ads + theatre_ads) / 3, "theatre_balance")
model.addConstr(mall_ads >= 0.2 * (mall_ads + bus_ads + theatre_ads), "mall_balance")


# Optimize model
model.optimize()

# Print results
if model.status == GRB.OPTIMAL:
    print(f"Optimal Viewership: {model.objVal}")
    print(f"Number of Mall Ads: {mall_ads.x}")
    print(f"Number of Bus Stop Ads: {bus_ads.x}")
    print(f"Number of Theatre Ads: {theatre_ads.x}")
elif model.status == GRB.INFEASIBLE:
    print("The model is infeasible.")
else:
    print(f"Optimization ended with status {model.status}")

```
