Entdecken Sie klassische Algorithmen: Greedy, Divide and Conquer, dynamische Programmierung usw.

1. Greedy-Algorithmus

Der Greedy-Algorithmus ist ein gängiges Algorithmusparadigma, das normalerweise zur Lösung von Optimierungsproblemen verwendet wird.
Der Greedy-Algorithmus ist ein Algorithmusparadigma, das bei jedem Auswahlschritt die optimale Entscheidung im aktuellen Zustand trifft. Die Kernidee besteht darin, in jedem Schritt die beste Lösung auszuwählen, um die endgültige globale optimale Lösung zu erreichen. Das Merkmal dieses Algorithmus besteht darin, dass er nur die lokal optimale Lösung berücksichtigt, ohne die getroffenen Entscheidungen zurückzuverfolgen oder zu überdenken, und er garantiert nicht, dass er alle Probleme lösen kann. Obwohl der Greedy-Algorithmus nicht für alle Probleme geeignet ist, ist die Idee des Greedy-Algorithmus für bestimmte Problemtypen einfach und effizient.

1. Intervallplanung

Themenbeschreibung:

Job j beginnt bei sj und endet bei fj

Zwei Jobs sind kompatibel, wenn sie sich nicht überschneiden.

Ziel: Finden Sie die größte Teilmenge miteinander kompatibler Jobs.

Analyse von Problemlösungsideen:

Um dieses Problem mithilfe eines Greedy-Algorithmus zu lösen, können wir die folgenden Schritte ausführen:

  1. Sortieren Sie die Jobliste nach der Endzeit des Jobs und stellen Sie sicher, dass die Jobs in aufsteigender Reihenfolge der Endzeit sortiert sind.
  2. Initialisieren Sie eine leere maximale gegenseitig kompatible Job-Teilmenge max_jobs_subset.
  3. Wählen Sie den ersten Job aus der sortierten Jobliste aus und fügen Sie ihn zu max_jobs_subset hinzu.
  4. Durchlaufen Sie die verbleibenden Jobs und führen Sie für jeden Job Folgendes durch:
    • Prüft, ob die Startzeit dieses Jobs nach der Endzeit des letzten Jobs in max_jobs_subset liegt.
    • Wenn ja, wird der Job zu max_jobs_subset hinzugefügt.
  5. Gibt max_jobs_subset als maximale gegenseitig kompatible Job-Teilmenge zurück.

Python-Code:

# 定义一个job类
class Job:
    def __init__(self, start_time, finsh_time):
        self.start_time = start_time
        self.finsh_time = finsh_time

def find_max_compatible_jobs(jobs):
    # 使用sort函数对jobs列表进行排序 这里按照结束时间升序排列
    jobs.sort(key=lambda x: x.finsh_time)
    max_jobs_subset = []
    last_time = float('-inf')
    for job in jobs:
        if job.start_time >=last_time:
            max_jobs_subset.append(job)
            last_time = job.finsh_time
    # 这里直接返回最大公共子集
    return max_jobs_subset
# 编写测试函数
# 用户首先输入作业的数量
while True:
    try:
        job_count = int(input('请输入作业总数:'))
        if job_count < 1:
            raise ValueError
        break
    except ValueError:
        print('作业总数必须是一个正整数')
#  定义工作列表存储用户输入的工作
jobs = []
# 依次输入每个作业的起始时间和结束时间
for i in range(job_count):
    while True:
        try:
            start_time = int(input('请输入第{}个作业的起始时间:'.format(i + 1)))
            finsh_time = int(input('请输入第{}个作业的结束时间:'.format(i + 1)))
            if finsh_time < start_time:
                raise ValueError
            break
        except ValueError:
            print('结束时间必须晚于开始时间')
    job = Job(start_time,finsh_time)
    jobs.append(job)
# 调用函数找到最大互相兼容作业子集
max_compatible_jobs = find_max_compatible_jobs(jobs)
print('相互兼容的最大工作子集:')
for job in max_compatible_jobs:
    print('作业开始时间:{},作业结束时间:{}'.format(job.start_time,job.finsh_time))

2. Intervallteilung

Themenbeschreibung:

Vorlesung j beginnt bei sj und endet bei fj;
Ziel: Finden Sie die Mindestanzahl von Klassenzimmern, um alle Vorlesungen so anzuordnen
, dass dies nicht der Fall ist Im selben Raum fanden gleichzeitig zwei Vorträge statt.

Analyse von Problemlösungsideen:

Der spezifische Lösungsprozess ist wie folgt:

  1. Um alle Vorlesungen nach Endzeit zu sortieren, können Sie die Funktion sorted verwenden und den Parameter key auf die Endzeit der Vorlesung setzen. Dadurch wird sichergestellt, dass wir im Greedy-Algorithmus die Vorlesung zuerst verarbeiten, die am frühesten endet.
  2. Erstellen Sie eine leere Klassenraumliste, um geplante Vorlesungen zu speichern.
  3. Durchlaufen Sie die sortierte Vorlesungsliste. Für jede Vorlesung müssen wir prüfen, ob ein Klassenraum vorhanden ist, der die Vorlesung aufnehmen kann.
  4. Für jede Vorlesung durchlaufen wir die bereits eingeplanten Unterrichtsräume und prüfen, ob die aktuelle Vorlesung in einem bestimmten Unterrichtsraum eingeplant werden kann. Wir können feststellen, ob die Veranstaltung im selben Klassenzimmer stattfinden kann, indem wir die Startzeit der aktuellen Vorlesung mit der Endzeit der geplanten Vorlesung vergleichen.
  5. Wenn ein Klassenzimmer vorhanden ist, in dem die aktuelle Vorlesung stattfinden kann, wird die Vorlesung zur Liste der Klassenzimmer hinzugefügt.
  6. Wenn kein Klassenzimmer vorhanden ist, in dem die aktuelle Vorlesung stattfinden kann, wird ein neues Klassenzimmer erstellt und die aktuelle Vorlesung dem neuen Klassenzimmer hinzugefügt.
  7. Nach dem Durchlaufen aller Vorlesungen wird die Länge der Klassenraumliste zurückgegeben, die der Mindestanzahl der erforderlichen Klassenräume entspricht.

Python-Code:

class Lecture:
    def __init__(self, start_time, end_time):
        self.start_time = start_time
        self.end_time = end_time
def get_valid_lectures():
    lectures = []
    while True:
        try:
            num_lectures = int(input("请输入讲座数量: "))
            if num_lectures <= 0:
                raise ValueError("讲座数量必须大于0")
            for i in range(num_lectures):
                start_time = int(input(f"请输入讲座{
      
      i + 1}的开始时间: "))
                end_time = int(input(f"请输入讲座{
      
      i + 1}的结束时间: "))
                if start_time >= end_time:
                    raise ValueError(f"讲座{
      
      i + 1}的开始时间必须早于结束时间")
                if start_time < 0:
                    raise ValueError(f'讲座{
      
      i+1}的开始时间必须是正数')
                if end_time < 0:
                    raise ValueError(f'讲座{
      
      i+1}的结束时间必须是正数')
                lecture = Lecture(start_time, end_time)
                lectures.append(lecture)
            break
        except ValueError as e:
            print("输入错误:", e)
    return lectures


def minimum_classrooms(lectures):
    sorted_lectures = sorted(lectures, key=lambda x: x.end_time)
    classrooms = []
    for lecture in sorted_lectures:
        found_classroom = False
        for classroom in classrooms:
            if lecture.start_time >= classroom[-1].end_time:
                classroom.append(lecture)
                found_classroom = True
                break

        if not found_classroom:
            classrooms.append([lecture])

    return len(classrooms)

# 获取有效的讲座数据
valid_lectures = get_valid_lectures()
# 调用函数获取最小教室数量
min_classrooms = minimum_classrooms(valid_lectures)
print("需要的最小教室数量:", min_classrooms)

3. Mindestverzögerungszeit

Themenbeschreibung:

・Eine einzelne Ressource verarbeitet jeweils einen Job.

・Job j benötigt tj Verarbeitungszeiteinheiten und läuft zum Zeitpunkt dj ab

・Wenn j zum Zeitpunkt sj beginnt, endet es zum Zeitpunkt fj=sj+tj

・Latenzzeit: ℓj=maximum {0, fj–dj}.

・Ziel: Planen Sie alle Jobs so, dass die maximale Verzögerung L=maxjℓj minimiert wird

Analyse von Problemlösungsideen:

  1. Sortieren Sie zunächst alle Aufgaben nach Frist dj, um sicherzustellen, dass Aufgaben mit der frühesten Frist zuerst bearbeitet werden.
  2. Erstellen Sie eine Leerlaufzeitvariable current_time und initialisieren Sie sie auf 0.
  3. Durchlaufen Sie jede Aufgabe in sortierter Reihenfolge:
    • Berechnen Sie die Abschlusszeit der aktuellen Aufgabe fj = current_time + tj, wobei tj die Bearbeitungszeit der aktuellen Aufgabe ist.
    • Berechnen Sie die Verzögerungszeit der aktuellen Aufgabe ℓj = max(0, fj - dj).
    • Aktualisieren Sie die maximale VerzögerungL, wenn die Verzögerungszeit der aktuellen Aufgabeℓj größer alsL ist wird L aktualisiert aufℓj.
    • Aktualisieren Sie die aktuelle Uhrzeit current_time und setzen Sie sie auf die Abschlusszeit der aktuellen Aufgabe fj.
  4. Maximale Rückkehrverzögerung L.

Python-Code:

class Job:
    def __init__(self, processing_time, deadline):
        self.processing_time = processing_time
        self.deadline = deadline

def minimum_lateness(jobs):
    jobs.sort(key=lambda x: x.deadline)  # 按照截止时间排序
    L = 0  # 最大延迟
    current_time = 0  # 当前时间
    for job in jobs:
        # 验证处理时间和截止时间的格式和数值
        if not isinstance(job.processing_time, int) or job.processing_time <= 0:
            raise ValueError("Processing time should be a positive integer.")
        if not isinstance(job.deadline, int) or job.deadline <= 0:
            raise ValueError("Deadline should be a positive integer.")

        # 计算完成时间和延迟时间
        finish_time = current_time + job.processing_time
        lateness = max(0, finish_time - job.deadline)

        # 更新最大延迟
        L = max(L, lateness)

        # 更新当前时间
        current_time = finish_time

    return L


# 用户输入数据
n = None
while not isinstance(n, int) or n <= 0:
    try:
        n = int(input("Enter the number of jobs: "))
        if n <= 0:
            print("Number of jobs should be a positive integer.")
    except ValueError:
        print("Invalid input. Number of jobs should be an integer.")

jobs = []
for i in range(n):
    print(f"Enter information for job {
      
      i+1}:")
    processing_time = None
    while not isinstance(processing_time, int) or processing_time <= 0:
        try:
            processing_time = int(input("Enter the processing time: "))
            if processing_time <= 0:
                print("Processing time should be a positive integer.")
        except ValueError:
            print("Invalid input. Processing time should be an integer.")

    deadline = None
    while not isinstance(deadline, int) or deadline <= 0:
        try:
            deadline = int(input("Enter the deadline: "))
            if deadline <= 0:
                print("Deadline should be a positive integer.")
        except ValueError:
            print("Invalid input. Deadline should be an integer.")

    job = Job(processing_time, deadline)
    jobs.append(job)

try:
    max_lateness = minimum_lateness(jobs)
    print("The maximum lateness is:", max_lateness)
except ValueError as e:
    print("Invalid input:", str(e))

2. Divide-and-Conquer-Algorithmus

Der Divide-and-Conquer-Algorithmus ist ein wichtiges Algorithmusparadigma, das ein großes Problem in mehrere kleine Probleme zerlegt, diese kleinen Probleme separat löst und sie dann zusammenführt, um die Lösung des ursprünglichen Problems zu erhalten. Die Kernidee besteht darin, das Problem rekursiv in kleinere Teilprobleme zu zerlegen und diese Teilprobleme unabhängig zu lösen.

Die Entwurfsidee des Divide-and-Conquer-Algorithmus gliedert sich üblicherweise in drei Schritte:

  1. Zerlegung: Zerlegen Sie das ursprüngliche Problem in mehrere Unterprobleme.
  2. Lösen: Lösen Sie diese Teilprobleme rekursiv.
  3. Zusammenführen: Füge die Lösungen der Unterprobleme zur Lösung des ursprünglichen Problems zusammen.

Der Divide-and-Conquer-Algorithmus wird häufig verwendet, um Probleme rekursiver Natur zu lösen, z. B. Zusammenführungssortierung, schnelle Sortierung, Probleme im Zusammenhang mit Binärbäumen usw. Obwohl es im Allgemeinen effizienter ist, ist es nicht für alle Probleme geeignet.

1. Aktuelle Kopplungsprobleme

Themenbeschreibung:

Aktuelle Kopplungsprobleme. Finden Sie bei gegebenen n Punkten auf der Ebene ein Punktpaar, bei dem der euklidische Abstand zwischen ihnen minimal ist.

Analyse von Problemlösungsideen:

Die Divide-and-Conquer-Methode ist eine Problemlösungsmethode, die das Problem in kleinere Teilprobleme aufteilt und dann die Lösungen für die Teilprobleme kombiniert, um eine Lösung für das ursprüngliche Problem zu erhalten. Hier sind die detaillierten Schritte zur Lösung des nächstgelegenen Paarungsproblems mithilfe der Divide-and-Conquer-Methode:

  1. Sortieren Sie alle Punkte nach ihrer x-Koordinate.
  2. Teilen Sie die Punktmenge in einen linken und einen rechten Teil auf, die als P_left bzw. P_right bezeichnet werden.
  3. Ermitteln Sie rekursiv den Abstand des nächstgelegenen Paares in P_left bzw. P_right, bezeichnet als d_left und d_right.
  4. Nehmen Sie den kleineren Wert von d_left und d_right und notieren Sie ihn als d.
  5. Suchen Sie in der linken und rechten Punktmenge die Punktmenge P_mid im mittleren Bereich. Die Differenz zwischen ihrer x-Koordinate und der x-Koordinate des Mittelpunkts ist kleiner oder gleich d.
  6. Sortieren Sie nach Y-Koordinate in P_mid.
  7. Durchlaufen Sie jeden Punkt in P_mid, und für jeden Punkt p muss nur der Abstand zu den darauffolgenden 7 Punkten berechnet werden (da im sortierten P_mid der Abstand zwischen zwei beliebigen Punkten höchstens d beträgt).
  8. Finden Sie den Abstand zwischen den beiden kleinsten Punkten in P_mid, bezeichnet als d_mid.
  9. Gibt das Minimum von d, d_left und d_right als endgültigen Abstand der nächsten Paarung zurück.

Python-Code:

import math
# 定义一个坐标类 包括x和y
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
# 求解两个点的欧几里得距离
def distance(p1, p2):
    return math.sqrt((p1.x-p2.x)**2+(p1.y-p2.y)**2)
# 使用分治法求解最近配对问题
def closest_pair(points):
    n = len(points)
#     这里如果n小于等于3 即直接使用暴力法计算
    if n <= 3:
        # 默认最小距离为最大值
        min_distance = float('inf')
        closest_points = None
        for i in range(n):
            for j in range(i+1, n):
                if distance(points[i], points[j]) < min_distance:
                    min_distance = distance(points[i], points[j])
                    closest_points = (points[i], points[j])
        #函数直接返回最短距离点对和最小距离
        return closest_points, min_distance
#     如果点集个数大于3 则使用分治法求解
#     首先对点集按照x升序排列
    points.sort(key=lambda p: p.x)
#     然后将点集分为两部分
    mid = n // 2
    left_points = points[:mid]
    right_points = points[mid:]
#     分别求解左 右区间的最小距离与最近点集
    closest_left, d_left = closest_pair(left_points)
    closest_right, d_right = closest_pair(right_points)
    if d_left < d_right:
        d = d_left
        closest_points = closest_left
    else:
        d = d_right
        closest_points = closest_right
    mid_x = (points[mid-1].x+points[mid].x)/2
    mid_points = [p for p in points if abs(p.x-mid_x) < d]
#     然后把这些点按照y值排序
    mid_points.sort(key=lambda p: p.y)
    min_distance = d
    for i in range(len(mid_points)):
        for j in range(i+1, min(i+8, len(mid_points))):# 最多计算后序7个点的距离
            if distance(points[i], points[j]) < d:
                min_distance = distance(points[i], points[j])
                closest_points = (points[i], points[j])
    return closest_points, min_distance
# 获取用户输入的点坐标
points = []
while True:
    x_input = input("请输入点的 x 坐标(输入 'end' 结束输入): ")
    if x_input == 'end':
        break
    y_input = input("请输入点的 y 坐标(输入 'end' 结束输入): ")
    if y_input == 'end':
        break
    try:
        x = float(x_input)
        y = float(y_input)
        # 进行校验逻辑,例如对 x 和 y 坐标进行合法性检查、范围检查等,根据需求自行定义
        # 如果校验通过,则创建 Point 对象并添加到 points 列表中
        point = Point(x, y)
        points.append(point)
    except ValueError:
        print("输入的坐标不合法,请重新输入")

# 调用 closest_pair() 函数获取最近配对的结果
closest_points, min_distance = closest_pair(points)
point1, point2 = closest_points

# 输出最近配对的距离和坐标
print("The closest pair distance is:", min_distance)
print("The closest pair coordinates are:", (point1.x, point1.y), (point2.x, point2.y))

2. Das k-te kleine Element

Themenbeschreibung:

Finden Sie bei gegebenen n Elementen in einem vollständig geordneten Universum das k-kleinste Element.

Analyse von Problemlösungsideen:

  1. Zuerst müssen wir ein geeignetes Bezugselement auswählen. Sie können das Basiselement mit verschiedenen Methoden auswählen, z. B. durch Auswahl des ersten Elements, des letzten Elements oder durch zufällige Auswahl eines Elements. Die Auswahl eines guten Basiselements kann die Effizienz des Algorithmus verbessern.
  2. Teilen Sie das Array in zwei Unterarrays auf, von denen eines Elemente enthält, die kleiner oder gleich dem Pivotelement sind, und das andere Elemente enthält, die größer als das Pivotelement sind. Dies kann erreicht werden, indem das Array durchlaufen wird und Elemente, die kleiner oder gleich dem Basiselement sind, in einem Subarray und Elemente, die größer als das Basiselement sind, in einem anderen Subarray platziert werden. Dieser Vorgang wird oft als Partitionierung bezeichnet.
  3. Dann müssen wir die Position des Basiselements im gesamten Array bestimmen. Dies kann durch die Erfassung der Position des Basiselements während des Trennvorgangs erreicht werden. Nehmen Sie an, dass die Position des Pivot-Elements Pivot_index ist.
  4. Jetzt können wir basierend auf der Beziehung zwischen Pivot_Index und k entscheiden, wo wir weitersuchen möchten:
    • Wenn Pivot_index == k-1, bedeutet dies, dass das Pivot-Element genau das k-kleinste Element ist und es zurückgibt.
    • Wenn Pivot-Index > k-1, bedeutet dies, dass sich das k-kleinste Element links vom Pivot-Element befindet und wir rekursiv nach dem k-kleinsten Element im linken Subarray suchen können.
    • Wenn Pivot_Index < k-1, bedeutet dies, dass sich das k-kleinste Element auf der rechten Seite des Pivot-Elements befindet und wir rekursiv nach dem k-pivot_index-1-kleinsten Element im rechten Subarray suchen können.
  5. Wenden Sie die obigen Schritte rekursiv an, bis das k-kleinste Element gefunden ist.

Python-Code-Implementierung:

def choose_pivot(arr, low, high):
    mid = (low + high) // 2
    if arr[low] <= arr[mid] <= arr[high] or arr[high] <= arr[mid] <= arr[low]:
        return mid
    elif arr[mid] <= arr[low] <= arr[high] or arr[high] <= arr[low] <= arr[mid]:
        return low
    else:
        return high

# 分区 分为小于基准元素和大于基准元素两个数组
def partition(arr, low, high):
    # 通过三数取中找到基准元素
    pivot_index = choose_pivot(arr, low, high)
    pivot = arr[pivot_index]
    i = low - 1
    arr[pivot_index], arr[high] = arr[high], arr[pivot_index]

    for j in range(low, high):
        if arr[j] <= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]

    arr[i+1], arr[high] = arr[high], arr[i+1]
    return i+1

def kth_smallest(arr, low, high, k):
    if low == high:
        return arr[low]
    pivot_index = partition(arr, low, high)
    if pivot_index == k-1:
        return arr[pivot_index]
    elif pivot_index > k-1:
        return kth_smallest(arr, low, pivot_index-1, k)
    else:
        return kth_smallest(arr, pivot_index+1, high, k)

# 用户输入数据
arr_valid = False
arr = []
while not arr_valid:
    arr = input("请输入由空格分隔的整数数组:").split()
    if arr:
        try:
            arr = [int(num) for num in arr]
            arr_valid = True
        except ValueError:
            print("输入无效,请重新输入整数数组。")
    else:
        print("输入数组不能为空。")

# 校验数据有效性
k_valid = False
k = 0
while not k_valid:
    k = input("请输入要查找的第几小的元素的值:")
    if k.isdigit():
        k = int(k)
        if 1 <= k <= len(arr):
            k_valid = True
        else:
            print("输入的值超出了数组范围,请重新输入。")
    else:
        print("输入无效,请重新输入一个整数。")

kth_smallest_element = kth_smallest(arr, 0, len(arr)-1, k)
print(f"The {
      
      k}th smallest element is: {
      
      kth_smallest_element}")

3. Algorithmus des Krankenhausarztes

Themenbeschreibung:

Der Krankenhaus-Arzt-Algorithmus ist ein Problemlösungsansatz zur optimalen Zuordnung von Ärzten zu Krankenhäusern. Der Algorithmus stellt sicher, dass jedes Krankenhaus über eine ausreichende Anzahl an Ärzten verfügt und ordnet jeden Arzt anhand bestimmter Kriterien dem entsprechenden Krankenhaus zu.

Analyse von Problemlösungsideen:

Hier sind die Schritte zur Lösung des Krankenhaus-Arzt-Zuweisungsproblems mithilfe des Stable-Ehe-Algorithmus:

  1. Initialisiert eine Präferenzliste von Ärzten und Krankenhäusern und markiert alle Ärzte und Krankenhäuser als nicht übereinstimmend.
  2. Für jeden Arzt werden die Ärzte entsprechend ihrer Präferenz für das Krankenhaus dem am meisten bevorzugten Krankenhaus vorgeschlagen. Jeder Arzt kann nur einem Krankenhaus vorschlagen.
  3. Die Auswahl der eingegangenen Vorschläge jedes Krankenhauses erfolgt anhand der Präferenzliste des Krankenhauses. Wenn noch kein Krankenhaus gefunden wurde, wird der Arzt diesem Krankenhaus zugeordnet. Wenn das Krankenhaus bereits über einen Arzt verfügt, vergleichen Sie die Krankenhauspräferenzen des aktuellen Arztes und des passenden Arztes und wählen Sie den beliebteren Arzt aus.
  4. Wenn ein Arzt einem Krankenhaus zugeordnet wird, werden sowohl der Arzt als auch das Krankenhaus als übereinstimmend markiert.
  5. Wenn ein Arzt abgelehnt wird oder die Kapazität des Krankenhauses erreicht ist, wechselt der Arzt zum nächsten bevorzugten Krankenhaus.
  6. Wiederholen Sie die Schritte 2 und 3, bis alle Ärzte dem Krankenhaus zugeordnet wurden.
  7. Das endgültige Ergebnis des Arzt-Krankenhaus-Matchings ist der optimale Krankenhaus-Arzt-Zuteilungsplan.

Python-Code:

def stable_marriage(doctors, hospitals):
    doctor_preferences = {
    
    }
    hospital_preferences = {
    
    }
    doctor_matches = {
    
    }
    hospital_matches = {
    
    }

    # 初始化医生和医院的偏好列表
    for doctor in doctors:
        doctor_preferences[doctor] = doctors[doctor]

    for hospital in hospitals:
        hospital_preferences[hospital] = hospitals[hospital]

    # 将所有医生和医院都标记为未匹配状态
    for doctor in doctors:
        doctor_matches[doctor] = None

    for hospital in hospitals:
        hospital_matches[hospital] = None

    while None in doctor_matches.values():
        for doctor in doctors:
            if doctor_matches[doctor] is None:
                for hospital in doctor_preferences[doctor]:
                    if hospital_matches[hospital] is None:
                        doctor_matches[doctor] = hospital
                        hospital_matches[hospital] = doctor
                        break
                    else:
                        current_match = hospital_matches[hospital]
                        if hospital_preferences[hospital].index(doctor) < hospital_preferences[hospital].index(current_match):
                            doctor_matches[doctor] = hospital
                            doctor_matches[current_match] = None
                            hospital_matches[hospital] = doctor
                            break

    return doctor_matches


# 用户输入医生和医院的偏好
doctors = {
    
    }
hospitals = {
    
    }

n_doctors = int(input("请输入医生人数:"))
n_hospitals = int(input("请输入医院数量:"))

for i in range(n_doctors):
    doctor_name = input(f"请输入第{
      
      i+1}位医生的姓名:")
    doctor_preferences = input(f"请输入{
      
      doctor_name}医生对医院的偏好(以空格分隔):").split()
    doctors[doctor_name] = doctor_preferences

for i in range(n_hospitals):
    hospital_name = input(f"请输入第{
      
      i+1}家医院的名称:")
    hospital_preferences = input(f"请输入{
      
      hospital_name}医院对医生的偏好(以空格分隔):").split()
    hospitals[hospital_name] = hospital_preferences

# 调用稳定婚姻算法
matches = stable_marriage(doctors, hospitals)

# 输出医生-医院匹配结果
for doctor, hospital in matches.items():
    print(f"{
      
      doctor} 匹配到 {
      
      hospital}")

4. Dynamische Programmierung

Dynamische Programmierung (Dynamic Programming) ist eine algorithmische Idee zur Lösung von Optimierungsproblemen, die für Probleme mit überlappenden Teilproblemen und optimalen Teilstruktureigenschaften geeignet ist. Es zerlegt das Problem in eine Reihe überlappender Teilprobleme und vermeidet wiederholte Berechnungen, indem es die Lösungen der Teilprobleme speichert, wodurch eine effiziente Lösung des gesamten Problems ermöglicht wird.

Dynamische Programmieralgorithmen folgen im Allgemeinen den folgenden Schritten:

  1. Bestimmen Sie den Zustand: Teilen Sie das ursprüngliche Problem in mehrere Unterprobleme auf und definieren Sie den Zustand, der die Lösung des Problems darstellt.

  2. Definieren Sie die Zustandsübergangsgleichung: Beschreiben Sie die Beziehung zwischen Teilproblemen durch die Rekursionsbeziehung, dh wie die Lösung des aktuellen Problems anhand der bekannten Lösungen der Teilprobleme berechnet wird.

  3. Bestimmen Sie die Anfangsbedingungen: Bestimmen Sie die Lösung des einfachsten Teilproblems als Ausgangspunkt für die Rekursion.

  4. Bestimmen Sie die Reihenfolge der Berechnung: Bestimmen Sie die Reihenfolge der Berechnung basierend auf den Abhängigkeiten zwischen Teilproblemen, normalerweise nach dem Bottom-Up-Prinzip.

  5. Berechnen Sie die optimale Lösung: Gemäß der Zustandsübergangsgleichung und gemäß der festgelegten Berechnungssequenz wird die optimale Lösung des Problems durch Rekursion berechnet.

  6. Konstruieren Sie die optimale Lösung: Konstruieren Sie die optimale Lösung für das ursprüngliche Problem basierend auf der berechneten optimalen Lösung und den gespeicherten Zustandsinformationen.

Die Kernidee des dynamischen Programmieralgorithmus besteht darin, die Lösungen von Teilproblemen zur Lösung größerer Probleme zu verwenden. Durch das Speichern der Lösungen von Teilproblemen werden wiederholte Berechnungen vermieden, wodurch die Effizienz des Algorithmus erheblich verbessert wird. Dynamische Programmierung wird häufig zur Lösung von Optimierungsproblemen verwendet, z. B. dem Problem des kürzesten Wegs, dem Rucksackproblem, der Sequenzausrichtung usw.

Klassische dynamische Programmieralgorithmen: Nahtschnitzerei; Textähnlichkeits- und Differenzvergleich; Spline-Kurvenzuweisung usw.

Im Folgenden sind einige klassische Probleme aufgeführt, die häufig mit dynamischer Programmierung gelöst werden:

  1. Fibonacci-Folge: Berechnet den Wert der n-ten Fibonacci-Zahl.
  2. Rucksackproblem: Wählen Sie in einem Rucksack mit einem bestimmten Fassungsvermögen einige Gegenstände aus, die Sie in den Rucksack stecken möchten, sodass der Gesamtwert der Gegenstände maximiert wird, ohne das Fassungsvermögen des Rucksacks zu überschreiten.
  3. Längste gemeinsame Teilsequenz: Ermitteln Sie anhand zweier Zeichenfolgen die Länge ihrer längsten gemeinsamen Teilsequenz.
  4. Längste ansteigende Teilsequenz: Finden Sie bei einer gegebenen Sequenz die längste Teilsequenz, sodass die Elemente in der Teilsequenz der Reihe nach aufsteigen.
  5. Matrixkettenmultiplikation: Bei einer Reihe von Matrizen wird durch entsprechendes Hinzufügen von Klammern die Anzahl der Matrixmultiplikationen minimiert.
  6. Problem des kürzesten Wegs: Finden Sie den kürzesten Weg vom Startpunkt zum Endpunkt in einem gewichteten gerichteten Diagramm.
  7. Maximale Subarray-Summe: Finden Sie bei einem gegebenen Array von Ganzzahlen ein zusammenhängendes Subarray mit der maximalen Summe.
  8. Längster palindromischer Teilstring: Finden Sie bei gegebenem String den längsten palindromischen Teilstring.
  9. Bearbeitungsabstand: Berechnet die Mindestanzahl der Bearbeitungsvorgänge, die erforderlich sind, um eine Zeichenfolge in eine andere umzuwandeln.
  10. Schneidstange: Schneiden Sie eine Stahlstange der Länge n in mehrere Segmente, um den Gesamtverkaufswert zu maximieren.

1. Gewichtete Intervallplanung

Problembeschreibung:

Arbeit j 从 s j s_{j} SjAnfang, bestehend f j f_{j} FjEnde, und jeder Job hat eine Gewichtung w j w_{j} Inj

Zwei Jobs sind kompatibel, wenn sie sich nicht überschneiden.

Ziel: Finden Sie die Teilmenge der miteinander kompatiblen Jobs mit der größten Gewichtung.

Ideen zur Problemlösung:

Bei der Lösung eines Planungsproblems mit gewichteten Intervallen hoffen wir, eine Reihe konsistenter Intervalle zu finden, deren Gesamtgewichtssumme maximal ist.

Dynamische Programmierung ist eine häufig verwendete Methode zur Lösung von Optimierungsproblemen. Bei der Lösung des gewichteten Intervallplanungsproblems können wir die Idee der dynamischen Programmierung nutzen, um schrittweise die optimale Lösung zu berechnen.

Zuerst sortieren wir alle Intervalle nach der Endzeit von klein nach groß. Dies liegt daran, dass Intervalle mit früheren Endzeiten ein größeres Potenzial für die Kompatibilität mit späteren Intervallen haben.

Als nächstes definieren wir ein dp-Array. dp[i] stellt die maximale Gewichtssumme dar, wenn das i-te Intervall das letzte Intervall ist. Wir müssen die Werte des dp-Arrays einzeln berechnen.

Für jedes Intervall i müssen wir zwei Situationen berücksichtigen:

  1. Das Intervall i ist nicht ausgewählt: Zu diesem Zeitpunkt ist dp[i] gleich dp[i-1], dh die maximale Gewichtssumme, wenn das vorherige Intervall das letzte Intervall ist.
  2. Intervall i ist ausgewählt: Zu diesem Zeitpunkt müssen wir das Intervall j finden, dessen vorherige Endzeit kleiner oder gleich der Startzeit des Intervalls i ist, sodass das Gewicht von dp [j] + Intervall i am größten ist. Daher müssen wir das vorherige Intervall j durchlaufen, ein solches Intervall finden und das Gewicht von dp[j]+Intervall i berechnen.

Schließlich aktualisieren wir dp[i] auf den größeren Wert der beiden Fälle, d. h. dp[i] = max(dp[i-1], dp[j]+das Gewicht des Intervalls i).

Durch eine solche Zustandsübertragung können wir den Wert des dp-Arrays einzeln berechnen. Schließlich ist das letzte Element des dp-Arrays die maximale Gewichtssumme.

Fassen Sie die Idee der dynamischen Programmierung zur Lösung der gewichteten Intervallplanung zusammen:

  1. Sortieren Sie alle Intervalle in aufsteigender Reihenfolge nach der Endzeit.
  2. Definieren Sie das dp-Array, die Länge ist die Anzahl der Intervalle und wird auf 0 initialisiert.
  3. Durchlaufen Sie jedes Intervall i und berechnen Sie den Wert von dp[i]:
    a. Wenn Intervall i nicht ausgewählt ist, ist dp[i] gleich dp[i-1] .
    b. Wenn Intervall i ausgewählt ist, suchen Sie das Intervall j, dessen vorherige Endzeit kleiner oder gleich der Startzeit von Intervall i ist, und berechnen Sie das Gewicht von dp[j]+Intervall i .
  4. Aktualisieren Sie dp[i] auf den größeren Wert der beiden oben genannten Fälle.
  5. Gibt das letzte Element des dp-Arrays zurück, das die maximale Gewichtssumme darstellt.

Ich hoffe, dass diese Idee zur Problemlösung Ihnen helfen kann, die Idee der dynamischen Programmierung zur Lösung von Problemen bei der gewichteten Intervallplanung besser zu verstehen.

Python-Code-Implementierung:

class Job:
    def __init__(self, start, end, weight):
        self.start = start
        self.end = end
        self.weight = weight


def weighted_interval_scheduling(jobs):
    jobs.sort(key=lambda x: x.end)  # 按照结束时间从小到大排序
    n = len(jobs)
    dp = [0] * n
    dp[0] = jobs[0].weight  # 初始化第一个作业的dp值为其权重

    for i in range(1, n):
        dp[i] = jobs[i].weight  # 初始化dp[i]为作业i的权重
        for j in range(i-1, -1, -1):
            if jobs[j].end <= jobs[i].start:
                dp[i] = max(dp[i], dp[j] + jobs[i].weight)  # 更新dp[i]的值

    return max(dp)  # 返回最大权重和

# 示例输入
jobs = [
    Job(1, 4, 3),
    Job(3, 5, 2),
    Job(0, 6, 4),
    Job(4, 7, 1),
    Job(3, 8, 2),
    Job(5, 9, 6),
    Job(6, 10, 4),
    Job(8, 11, 2)
]

# 输出最大权重和
print(weighted_interval_scheduling(jobs))

2. Maximales Subarray-Problem

Problembeschreibung:
Das Problem des maximalen Subarrays bezieht sich darauf, ein kontinuierliches Subarray in einem Array zu finden, sodass die Summe des Subarrays am größten ist.

Ideenanalyse:

Das Folgende ist eine allgemeine Idee für die Verwendung dynamischer Programmierung zur Lösung des Maximum-Subarray-Problems:

  1. Definieren Sie ein dp-Array mit der gleichen Länge wie das ursprüngliche Array, das zum Speichern der größten Subarray-Summe verwendet wird, die an der aktuellen Position endet.
  2. Das erste Element des initialisierten dp-Arrays ist das erste Element des ursprünglichen Arrays, d. h. dp[0] = nums[0].
  3. Durchlaufen Sie das ursprüngliche Array ab Position 1 und berechnen Sie die maximale Summe der Unterarrays, die an der aktuellen Position enden.
    • Wenn dp[i-1] größer als 0 ist, bedeutet dies, dass die größte Subarray-Summe, die an der vorherigen Position endet, zur Subarray-Summe an der aktuellen Position beiträgt, also dp[i] = dp[i-1] + nums[i ] .
    • Wenn dp[i-1] kleiner oder gleich 0 ist, bedeutet dies, dass die größte Subarray-Summe, die an der vorherigen Position endet, nicht zur Subarray-Summe an der aktuellen Position beiträgt, also dp[i] = nums[i].
  4. Durchlaufen Sie das dp-Array und finden Sie die größte Subarray-Summe, d. h. max_sum = max(dp).
  5. Gibt max_sum zurück, was die maximale Subarray-Summe ist.

Python-Code-Implementierung:

def max_subarray(nums):
    n = len(nums)
    if n == 0:
        return 0

    max_sum = nums[0]  # 初始化最大子数组和为第一个元素
    curr_sum = nums[0]  # 当前位置的最大子数组和

    for i in range(1, n):
        if curr_sum > 0:
            curr_sum += nums[i]
        else:
            curr_sum = nums[i]

        max_sum = max(max_sum, curr_sum)

    return max_sum

# 用户输入和数据验证
while True:
    try:
        nums = [int(x) for x in input("请输入一个整数数组,数字之间用空格分隔:").split()]
        break
    except ValueError:
        print("输入无效,请重新输入整数数组!")

# 输出最大子数组和
print(max_subarray(nums))

Basierend auf dieser Frage wird eine neue Frage abgeleitet:

Ziel: Finden Sie bei einer gegebenen n-mal-n-Matrix ein Rechteck, dessen Summe das Maximum ist

Um mithilfe der dynamischen Programmierung das Rechteck mit der größten Summe in einer gegebenen n × n-Matrix zu finden, können Sie die folgenden Schritte ausführen:

  1. Definieren Sie ein dp-Array der Größe n × n, das die Summe der Rechtecke mit der unteren rechten Ecke an jeder Position in der Matrix speichert.
  2. Initialisieren Sie die erste Zeile und die erste Spalte des dp-Arrays mit den Elementen der ersten Zeile und der ersten Spalte der Matrix.
  3. Beginnen Sie mit der zweiten Zeile und Spalte und durchlaufen Sie jede Position (i, j) in der Matrix:
    • Berechnen Sie für jede Position die Summe des Rechtecks ​​mit der Position als untere rechte Ecke, d. h. d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p ​​[ i ] [ j − 1 ] − d p [ i − 1 ] [ j − 1 ] + m a t r i x [ i ] [ j ] dp[i][j] = dp[i-1][j ] + dp[i ][j-1] - dp[i-1][j-1] + Matrix[i][j] dp[i] [j]=dp[i1][j]+dp[i] [j1]dp[i1][j1]+matrix [i][j ]
  4. Durchlaufen Sie das dp-Array und ermitteln Sie die größte Summe von Rechtecken.
  5. Gibt die größte Rechtecksumme zurück.

Python-Code-Implementierung:

def max_rectangle_sum(matrix):
    n = len(matrix)
    if n == 0:
        return 0
    
    dp = [[0] * n for _ in range(n)]  # 定义 dp 数组
    
    # 初始化第一行和第一列
    dp[0][0] = matrix[0][0]
    for i in range(1, n):
        dp[i][0] = dp[i-1][0] + matrix[i][0]
        dp[0][i] = dp[0][i-1] + matrix[0][i]
    
    # 计算 dp 数组
    for i in range(1, n):
        for j in range(1, n):
            dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + matrix[i][j]
    
    max_sum = float('-inf')  # 初始化最大矩形和为负无穷大
    
    # 遍历 dp 数组,找到最大矩形和
    for i in range(n):
        for j in range(n):
            for k in range(i, n):
                for l in range(j, n):
                    curr_sum = dp[k][l]
                    if i > 0:
                        curr_sum -= dp[i-1][l]
                    if j > 0:
                        curr_sum -= dp[k][j-1]
                    if i > 0 and j > 0:
                        curr_sum += dp[i-1][j-1]
                    max_sum = max(max_sum, curr_sum)
    
    return max_sum

# 示例输入
matrix = [
    [1, 2, -1],
    [-3, 0, 6],
    [7, 8, -9]
]

# 输出最大矩形和
print(max_rectangle_sum(matrix))

3. Rucksackproblem

Problembeschreibung:

Das Rucksackproblem ist ein klassisches kombinatorisches Optimierungsproblem, das darauf abzielt, aus einer Menge von Gegenständen Gegenstände auszuwählen, die zum Platzieren in einem Rucksack geeignet sind, um den Gesamtwert zu maximieren und gleichzeitig zu verlangen, dass die Kapazität des Rucksacks einen bestimmten Grenzwert nicht überschreitet.

Umsetzungsideen:

  1. Definieren Sie ein eindimensionales Array dp mit der Größe (W+1), wobei W die Kapazität des Rucksacks ist. dp[j] stellt den Maximalwert dar, wenn die Rucksackkapazität j beträgt.
  2. Initialisieren Sie das dp-Array auf 0, d. h. dp[j] = 0.
  3. Durchlaufen Sie das dp-Array beginnend bei Position 1 und berechnen Sie den Maximalwert für jede Position.
    • Für jede Position j müssen wir bestimmen, ob das Gewicht des aktuellen Gegenstands kleiner oder gleich der Rucksackkapazität j ist. Wenn ja, können Sie den Artikel in den Rucksack legen. Der Maximalwert beträgt zu diesem Zeitpunkt max(dp[j], dp[j-w] + v), wobei w das Gewicht des aktuellen Artikels und v das Gewicht des aktuellen Artikels ist Wert des aktuellen Artikels.
    • Wir aktualisieren den berechneten Maximalwert in dp[j].
  4. Nach dem Durchlaufen des gesamten dp-Arrays ist dp[W] der maximale Rucksackwert.
  5. Die im Rucksack platzierten Gegenstände können durch Zurückverfolgen des dp-Arrays gefunden werden.
    • Wir beginnen an der letzten Position W. Wenn dp[j] != dp[j-w] + v, bedeutet dies, dass der j-te Gegenstand in den Rucksack gelegt wird.
    • Dann subtrahieren wir das Gewicht des Artikels von j und setzen die Suche vorwärts fort.
    • Wiederholen Sie die obigen Schritte, bis die erste Position 1 gesucht ist und die in den Rucksack gelegten Gegenstände erhalten werden können.
  6. Gibt den Maximalwert und die im Rucksack platzierten Gegenstände zurück.

Python-Code-Implementierung:

def knapsack_dp(weight, value, capacity):
    n = len(weight)
    if n == 0 or capacity == 0:
        return 0, []

    # 将浮点数转换为整数,乘以一个大的倍数,以保留小数点后的精度
    multiplier = 100
    weight = [int(w * multiplier) for w in weight]
    capacity = int(capacity * multiplier)

    dp = [0] * (capacity + 1)
    picks = []

    for i in range(n):
        if weight[i] <= capacity:
            for j in range(capacity, weight[i] - 1, -1):
                if dp[j] < dp[j - weight[i]] + value[i]:
                    dp[j] = dp[j - weight[i]] + value[i]

    max_value = dp[capacity]

    # 回溯找到放入背包的物品
    j = capacity
    for i in range(n - 1, -1, -1):
        if j >= weight[i] and dp[j] == dp[j - weight[i]] + value[i]:
            picks.append(i)
            j -= weight[i]

    picks.reverse()

    return max_value, picks


# 输入数据
n = int(input("请输入物品数量:"))
weight = []
value = []
for i in range(n):
    w = float(input(f"请输入第 {
      
      i+1} 个物品的重量:"))
    v = float(input(f"请输入第 {
      
      i+1} 个物品的价值:"))
    weight.append(w)
    value.append(v)
capacity = float(input("请输入背包的容量:"))

# 数据合法性验证
if n <= 0 or capacity < 0:
    print("输入的物品数量或背包容量不合法,请重新输入!")
else:
    max_value, picks = knapsack_dp(weight, value, capacity)
    print("最大价值为:", max_value)
    print("放入背包的物品索引:", picks)

4. Längstes gemeinsames Teilsequenzproblem

Problembeschreibung:

Ermitteln Sie anhand zweier Zeichenfolgen die Länge ihrer längsten gemeinsamen Teilsequenz.

Ideen zur Problemlösung:

  1. Definitionsproblem:
    • Eingabe: zwei Zeichenfolgen s1 und s2.
    • Ausgabe: Die Länge der längsten gemeinsamen Teilsequenz.
  2. Erstellen Sie ein zweidimensionales Array dp mit der Größe (len(s1)+1) × (len(s2)+1), um die Länge der längsten gemeinsamen Teilsequenz zu speichern.
    • dp[i][j] stellt die ersten Zeichen der Zeichenfolge s1 und die ersten = dar Die Länge der längsten gemeinsamen Teilsequenz von 4> Zeichen. is2j
  3. Randbedingungen initialisieren:
    • Wenni=0 oder j=0, dp[i][j] = 0 bedeutet, dass, wenn eine Zeichenfolge leer ist, die längste gemeinsame Teilsequenz Die Länge beträgt 0.
  4. Die dynamische Programmierung berechnet die Länge der längsten gemeinsamen Teilsequenz:
    • i=1j=1Start, Schritt-für-Schritt-Berechnung dp[i][j].
    • Wenns1[i-1] gleichs2[j-1] ist, können diese beiden Zeichen zur längsten gemeinsamen Teilsequenz hinzugefügt werden, also dp[i][j] = dp[i-1][j-1] + 1 .
    • Wenns1[i-1] nicht gleichs2[j-1] ist, muss es vor Zeichen und die ersten Zeichen von oder das erste von Finden Sie die längste gemeinsame Teilsequenz unter und nehmen Sie den größeren Wert der beiden, also . Zeichen von Zeichen und die ersten s1i-1s2js1is2j-1dp[i][j] = max(dp[i-1][j], dp[i][j-1])
  5. Gibt die Länge der längsten gemeinsamen Teilsequenz zurück:
    • dp[len(s1)][len(s2)]Das ist die Länge der längsten gemeinsamen Teilfolge.

Python-Code:

def longest_common_subsequence(s1, s2):
    m = len(s1)
    n = len(s2)

    # 创建一个二维数组来保存最长公共子序列的长度
    dp = [[0] * (n + 1) for _ in range(m + 1)]

    # 动态规划计算最长公共子序列的长度
    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if s1[i - 1] == s2[j - 1]:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

    # 返回最长公共子序列的长度
    return dp[m][n]


# 用户输入和数据验证
s1 = input("请输入第一个字符串:")
s2 = input("请输入第二个字符串:")

if not s1 or not s2:
    print("输入字符串不能为空")
else:
    lcs_length = longest_common_subsequence(s1, s2)
    print("最长公共子序列的长度为:", lcs_length)

Guess you like

Origin blog.csdn.net/qq_51447436/article/details/134295830