To solve this optimization problem, we first need to define the decision variables and the objective function. Let's denote:
- $P$ as the number of ads on planes,
- $B$ as the number of ads on blimps, and
- $H$ as the number of ads on hot air balloons.

The objective is to maximize viewership. Given that each ad on a plane reaches 100,000 viewers, each ad on a blimp reaches 50,000 viewers, and each ad on a hot air balloon reaches 20,000 viewers, the total viewership can be represented as $100,000P + 50,000B + 20,000H$.

The constraints are:
1. The budget constraint: $5,000P + 2,000B + 1,000H \leq 50,000$.
2. The limit on the number of ads on planes from the same company: $P \leq 5$.
3. At most half the total number of ads can occur on hot air balloons: $H \leq 0.5(P + B + H)$.
4. At least 20% of the ads should occur on blimps: $B \geq 0.2(P + B + H)$.

Now, let's translate these constraints and the objective function into Gurobi code in Python:

```python
from gurobipy import *

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

# Define decision variables
P = m.addVar(vtype=GRB.INTEGER, name="ads_on_planes")
B = m.addVar(vtype=GRB.INTEGER, name="ads_on_blimps")
H = m.addVar(vtype=GRB.INTEGER, name="ads_on_hot_air_balloons")

# Objective function: Maximize viewership
m.setObjective(100000*P + 50000*B + 20000*H, GRB.MAXIMIZE)

# Constraints
m.addConstr(5000*P + 2000*B + 1000*H <= 50000, name="budget_constraint")
m.addConstr(P <= 5, name="plane_ads_limit")
m.addConstr(H <= 0.5*(P + B + H), name="hot_air_balloons_limit")
m.addConstr(B >= 0.2*(P + B + H), name="blimps_minimum")

# Solve the model
m.optimize()

# Print solution
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Ads on planes: {P.x}")
    print(f"Ads on blimps: {B.x}")
    print(f"Ads on hot air balloons: {H.x}")
else:
    print("No optimal solution found")
```