# Complete GUROBIPY implementation - Retry Attempt 4

import gurobipy as gp
from gurobipy import GRB

def ship_mission_optimization():
    """Optimize ship assignments to missions to minimize operational costs."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("ship_mission")
    
    # Data: Ships and their costs
    ships = [1, 2, 3]
    missions = [101, 102, 103]
    
    # Costs and tonnage for each ship
    cost_per_knot = {1: 11.0, 2: 13.0, 3: 12.0}
    tonnage = {1: 1500.0, 2: 2500.0, 3: 1800.0}
    cost_per_ton = {1: 5.5, 2: 6.0, 3: 5.8}
    
    # Validate data lengths
    assert len(cost_per_knot) == len(tonnage) == len(cost_per_ton) == len(ships), "Array length mismatch"
    
    # 2. VARIABLES
    # Binary decision variables for ship-mission assignments
    x = model.addVars(ships, missions, vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    # Minimize total operational cost
    model.setObjective(
        gp.quicksum((cost_per_knot[i] + tonnage[i] * cost_per_ton[i]) * x[i, j] for i in ships for j in missions),
        GRB.MINIMIZE
    )
    
    # 4. CONSTRAINTS
    # Each mission must be covered by exactly one ship
    model.addConstrs((gp.quicksum(x[i, j] for i in ships) == 1 for j in missions), name="mission_coverage")
    
    # Each ship can be assigned to at most one mission
    model.addConstrs((gp.quicksum(x[i, j] for j in missions) <= 1 for i in ships), name="ship_assignment")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in ships:
            for j in missions:
                if x[i, j].x > 1e-6:
                    print(f"Ship {i} assigned to Mission {j}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
ship_mission_optimization()