To solve the given optimization problem, we first need to define the decision variables and the objective function. Let's denote:

- $x_m$ as the number of ads in malls,
- $x_b$ as the number of ads at bus stops, and
- $x_t$ as the number of ads in theatres.

The objective is to maximize viewership. Given that an ad in a mall reaches 50,000 viewers, an ad at a bus stop reaches 10,000 viewers, and an ad in a theatre reaches 20,000 viewers, the total viewership $V$ can be represented as:

\[ V = 50000x_m + 10000x_b + 20000x_t \]

The constraints are:
1. The weekly advertising budget is $30,000. Given the costs of ads in each area ($5,000 for a mall ad, $1,000 for a bus stop ad, and $3,000 for a theatre ad), we have:

\[ 5000x_m + 1000x_b + 3000x_t \leq 30000 \]

2. The city limits the number of ads at a bus stop from a single company to 20:

\[ x_b \leq 20 \]

3. At most a third of the total number of ads should be in theatres:

\[ x_t \leq \frac{1}{3}(x_m + x_b + x_t) \]
\[ 3x_t \leq x_m + x_b + x_t \]
\[ 2x_t \leq x_m + x_b \]

4. A minimum of 20% of ads should be in malls:

\[ x_m \geq 0.20(x_m + x_b + x_t) \]
\[ x_m \geq \frac{1}{5}(x_m + x_b + x_t) \]
\[ 5x_m \geq x_m + x_b + x_t \]
\[ 4x_m \geq x_b + x_t \]

Now, let's translate these equations into Gurobi code in Python:

```python
from gurobipy import *

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

# Decision variables
x_m = m.addVar(vtype=GRB.INTEGER, name="Mall_ads")
x_b = m.addVar(vtype=GRB.INTEGER, name="Bus_stop_ads", ub=20)
x_t = m.addVar(vtype=GRB.INTEGER, name="Theatre_ads")

# Objective function: Maximize viewership
m.setObjective(50000*x_m + 10000*x_b + 20000*x_t, GRB.MAXIMIZE)

# Constraints
m.addConstr(5000*x_m + 1000*x_b + 3000*x_t <= 30000, name="Budget")
m.addConstr(x_b <= 20, name="Bus_stop_limit")
m.addConstr(2*x_t <= x_m + x_b, name="Theatre_max_proportion")
m.addConstr(4*x_m >= x_b + x_t, name="Mall_min_proportion")

# Optimize the model
m.optimize()

# Print results
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Mall ads: {x_m.x}")
    print(f"Bus stop ads: {x_b.x}")
    print(f"Theatre ads: {x_t.x}")
else:
    print("No optimal solution found.")
```