feat: Implement Min Path Sum, Flower Planting, and Advantage Shuffle problems with corresponding backend and frontend components.

This commit is contained in:
hissin 2025-11-20 14:50:45 +08:00
parent 3dedaf0899
commit 8ee91428cd
18 changed files with 1544 additions and 0 deletions

View File

@ -24,3 +24,11 @@ from routers import n_queens_router
app.include_router(n_queens_router.router)
from routers import knapsack_router
app.include_router(knapsack_router.router)
from routers import min_path_sum_router
app.include_router(min_path_sum_router.router)
from routers import flower_planting_router
app.include_router(flower_planting_router.router)
from routers import advantage_shuffle_router
app.include_router(advantage_shuffle_router.router)

View File

@ -0,0 +1,7 @@
import random
from typing import List
def generate_advantage_shuffle_case(size: int = 10, max_val: int = 100) -> dict:
nums1 = [random.randint(0, max_val) for _ in range(size)]
nums2 = [random.randint(0, max_val) for _ in range(size)]
return {"nums1": nums1, "nums2": nums2}

View File

@ -0,0 +1,50 @@
from typing import Any, Dict, List
from common.base_problem import BaseProblem
from problems.advantage_shuffle.generator import generate_advantage_shuffle_case
from problems.advantage_shuffle.solvers import solve_greedy, solve_brute_force, solve_random
from common.timer_utils import time_execution
class AdvantageShuffleProblem(BaseProblem):
def generate_case(self, size: int = 10, max_val: int = 100) -> Dict[str, Any]:
return generate_advantage_shuffle_case(size, max_val)
def solve(self, input_data: Dict[str, Any], algorithm: str) -> Dict[str, Any]:
nums1 = input_data.get("nums1", [])
nums2 = input_data.get("nums2", [])
result = None
duration = 0.0
if algorithm == "greedy":
result, duration = time_execution(solve_greedy)(nums1, nums2)
elif algorithm == "brute_force":
result, duration = time_execution(solve_brute_force)(nums1, nums2)
elif algorithm == "random":
result, duration = time_execution(solve_random)(nums1, nums2)
else:
raise ValueError(f"Unknown algorithm: {algorithm}")
# Calculate advantage score
score = sum(1 for a, b in zip(result, nums2) if a > b)
return {
"algorithm": algorithm,
"shuffled_nums1": result,
"advantage_score": score,
"time_seconds": duration
}
def verify(self, input_data: Any, result: Any) -> bool:
# Verification is hard because there might be multiple optimal solutions.
# We can verify that the score matches the greedy score (which is optimal).
nums1 = input_data.get("nums1", [])
nums2 = input_data.get("nums2", [])
greedy_res, _ = time_execution(solve_greedy)(nums1, nums2)
max_score = sum(1 for a, b in zip(greedy_res, nums2) if a > b)
# If algorithm claims a score, is it correct based on its output?
output = result.get("shuffled_nums1", [])
actual_score = sum(1 for a, b in zip(output, nums2) if a > b)
return actual_score == result.get("advantage_score")

View File

@ -0,0 +1,117 @@
from typing import List
def solve_greedy(nums1: List[int], nums2: List[int]) -> List[int]:
"""
Greedy algorithm (Tian Ji Horse Racing strategy).
Sort nums1. Sort nums2 but keep track of original indices.
O(N log N)
"""
n = len(nums1)
sorted_nums1 = sorted(nums1)
# Store (value, original_index) for nums2 to reconstruct answer order
sorted_nums2 = sorted([(val, i) for i, val in enumerate(nums2)])
res = [0] * n
# Two pointers for nums2 (weakest horse, strongest horse)
left_2, right_2 = 0, n - 1
# Iterate through our horses (nums1) from weakest to strongest?
# Actually standard implementation:
# Compare our weakest with their weakest.
# If ours > theirs, use it (win).
# Else, use our weakest to burn their strongest (lose).
# Let's use a deque or two pointers on nums1 as well?
# Actually, iterating nums1 from start to end (weakest to strongest) works with logic:
# If current nums1 can beat current weakest nums2, do it.
# Else, sacrifice current nums1 against current strongest nums2.
# Re-verification of strategy:
# We want to maximize wins.
# Consider our sorted nums1.
# Consider sorted nums2.
# If nums1[smallest] > nums2[smallest], we win! Assign nums1[smallest] to nums2[smallest]'s slot.
# If not, nums1[smallest] is useless for winning against weakest. Is it useful for anything?
# No, it's the worst horse. Sacrifice it against nums2[largest].
left_1, right_1 = 0, n - 1
left_2, right_2 = 0, n - 1
# We need to assign values to the original indices of nums2
# Wait, standard approach iterates nums1 (sorted) or nums2 (sorted)?
# Let's process nums1 from strongest to weakest? Or weakest to strongest?
# Let's use the standard LC 870 approach:
# Sort A. Sort B (with indices).
# For each b in sorted B (weakest to strongest):
# pick smallest a > b.
# If exists, use it.
# If not, use smallest a available (sacrifice).
# Better Tian Ji Logic:
# Compare our fastest with their fastest.
# If ours > theirs, win.
# Else (ours <= theirs), compare our slowest with their slowest.
# If ours > theirs, win.
# Else (ours <= theirs), race our slowest against their fastest.
# Let's implement the standard Greedy for "Advantage Shuffle" (Maximize A[i] > B[i] count):
# 1. Sort A.
# 2. Sort B with indices.
# 3. Use deque for A.
# Iterate B from strongest to weakest?
# Actually:
# Iterate B sorted (strongest to weakest).
# If A's strongest > B's strongest: Match them.
# Else: A's strongest cannot beat B's strongest. Nothing can.
# So sacrifice A's weakest against B's strongest.
import collections
deque_nums1 = collections.deque(sorted_nums1)
sorted_nums2_desc = sorted([(val, i) for i, val in enumerate(nums2)], key=lambda x: -x[0])
for val_b, idx_b in sorted_nums2_desc:
if deque_nums1[-1] > val_b:
res[idx_b] = deque_nums1.pop() # Strongest vs Strongest (Win)
else:
res[idx_b] = deque_nums1.popleft() # Weakest vs Strongest (Sacrifice)
return res
def solve_random(nums1: List[int], nums2: List[int]) -> List[int]:
"""
Random shuffling algorithm (Naive).
O(N) per attempt, but effectively random performance.
Just returns a random permutation of nums1.
"""
import random
res = nums1[:]
random.shuffle(res)
return res
def solve_brute_force(nums1: List[int], nums2: List[int]) -> List[int]:
"""
Try all permutations of nums1 to find max advantage.
O(N!) - Extremely slow.
"""
import itertools
n = len(nums1)
# Safety limit
if n > 8:
return solve_greedy(nums1, nums2)
max_score = -1
best_perm = []
for perm in itertools.permutations(nums1):
score = sum(1 for a, b in zip(perm, nums2) if a > b)
if score > max_score:
max_score = score
best_perm = list(perm)
if score == n: # Optimization: max possible found
break
return best_perm

View File

@ -0,0 +1,37 @@
import random
from typing import List
def generate_flower_planting_case(length: int = 10, chance: float = 0.3) -> List[int]:
"""
Generates a flowerbed array of 0s and 1s.
0: Empty, 1: Planted.
Ensures no adjacent 1s (valid initial state).
"""
flowerbed = [0] * length
for i in range(length):
if random.random() < chance:
# Check constraints
prev_empty = (i == 0) or (flowerbed[i-1] == 0)
# Since we fill left-to-right, we only check left neighbor carefully.
# But we must also ensure we don't invalidate a future planting?
# Actually, just randomly placing 1s might create invalid state (1,1).
# So we place 1 only if i-1 is 0.
if prev_empty:
flowerbed[i] = 1
# Double check right neighbor constraint (though we fill sequentially,
# randomness might need a second pass or careful construction.
# Simplified: construction guarantees i-1 is 0.
# But what if we place at i, and then at i+1?
# The loop above allows that. Let's fix logic.
flowerbed = [0] * length
i = 0
while i < length:
if random.random() < chance:
flowerbed[i] = 1
i += 2 # Skip next spot to maintain validity
else:
i += 1
return flowerbed

View File

@ -0,0 +1,46 @@
from typing import Any, Dict, List
import random
from common.base_problem import BaseProblem
from problems.flower_planting.generator import generate_flower_planting_case
from problems.flower_planting.solvers import solve_greedy, solve_brute_force, max_flowers_greedy
from common.timer_utils import time_execution
class FlowerPlantingProblem(BaseProblem):
def generate_case(self, length: int = 10, n: int = 0) -> Dict[str, Any]:
flowerbed = generate_flower_planting_case(length)
# If n is not provided (or 0), generate a challenging but possible n
# We calculate max possible, then take a random portion of it or just use it.
# Let's just return the bed and let user/frontend specify n, OR generate a random N.
# Problem requires checking if n can be planted.
if n == 0:
max_possible = max_flowers_greedy(flowerbed)
# Randomly pick n between 0 and max_possible + 1 (to sometimes return False)
n = random.randint(0, max_possible + 1) if max_possible > 0 else 1
return {"flowerbed": flowerbed, "n": n}
def solve(self, input_data: Dict[str, Any], algorithm: str) -> Dict[str, Any]:
flowerbed = input_data.get("flowerbed", [])
n = input_data.get("n", 1)
result = None
duration = 0.0
if algorithm == "greedy":
result, duration = time_execution(solve_greedy)(flowerbed, n)
elif algorithm == "brute_force":
result, duration = time_execution(solve_brute_force)(flowerbed, n)
else:
raise ValueError(f"Unknown algorithm: {algorithm}")
return {
"algorithm": algorithm,
"can_plant": result,
"time_seconds": duration
}
def verify(self, input_data: Any, result: Any) -> bool:
flowerbed = input_data.get("flowerbed", [])
n = input_data.get("n", 1)
truth, _ = time_execution(solve_greedy)(flowerbed, n)
return result.get("can_plant") == truth

View File

@ -0,0 +1,76 @@
from typing import List
def solve_greedy(flowerbed: List[int], n: int) -> bool:
"""
Greedy algorithm: Plant whenever possible.
O(N)
"""
count = 0
# Operate on a copy to avoid modifying input in place if passed by reference
bed = flowerbed[:]
length = len(bed)
for i in range(length):
if bed[i] == 0:
empty_left = (i == 0) or (bed[i-1] == 0)
empty_right = (i == length - 1) or (bed[i+1] == 0)
if empty_left and empty_right:
bed[i] = 1
count += 1
if count >= n:
return True
return count >= n
def solve_brute_force(flowerbed: List[int], n: int) -> bool:
"""
Backtracking/Brute Force to see if N flowers can be planted.
O(2^N) - Highly inefficient, good for contrast.
"""
length = len(flowerbed)
def can_plant(bed, index, remaining):
if remaining == 0:
return True
if index >= length:
return False
# Try planting at index
if bed[index] == 0:
empty_left = (index == 0) or (bed[index-1] == 0)
empty_right = (index == length - 1) or (bed[index+1] == 0)
if empty_left and empty_right:
# Option 1: Plant here
bed[index] = 1
if can_plant(bed, index + 2, remaining - 1):
return True
bed[index] = 0 # Backtrack
# Option 2: Don't plant here, try next spot
if can_plant(bed, index + 1, remaining):
return True
return False
# Safety for large inputs
if length > 25:
# Fallback
return solve_greedy(flowerbed, n)
return can_plant(flowerbed[:], 0, n)
# Helper for just counting max flowers (often the core logic needed)
def max_flowers_greedy(flowerbed: List[int]) -> int:
count = 0
bed = flowerbed[:]
length = len(bed)
for i in range(length):
if bed[i] == 0:
empty_left = (i == 0) or (bed[i-1] == 0)
empty_right = (i == length - 1) or (bed[i+1] == 0)
if empty_left and empty_right:
bed[i] = 1
count += 1
return count

View File

@ -0,0 +1,8 @@
import random
from typing import List
def generate_min_path_sum_case(rows: int = 5, cols: int = 5, min_val: int = 1, max_val: int = 20) -> List[List[int]]:
"""
Generates a rows x cols grid with random integers.
"""
return [[random.randint(min_val, max_val) for _ in range(cols)] for _ in range(rows)]

View File

@ -0,0 +1,34 @@
from typing import Any, Dict, List
from common.base_problem import BaseProblem
from problems.min_path_sum.generator import generate_min_path_sum_case
from problems.min_path_sum.solvers import solve_dp, solve_recursion
from common.timer_utils import time_execution
class MinPathSumProblem(BaseProblem):
def generate_case(self, rows: int = 5, cols: int = 5, min_val: int = 1, max_val: int = 20) -> Dict[str, Any]:
grid = generate_min_path_sum_case(rows, cols, min_val, max_val)
return {"grid": grid}
def solve(self, input_data: Dict[str, Any], algorithm: str) -> Dict[str, Any]:
grid = input_data.get("grid", [])
result = None
duration = 0.0
if algorithm == "dp":
result, duration = time_execution(solve_dp)(grid)
elif algorithm == "recursion":
result, duration = time_execution(solve_recursion)(grid)
else:
raise ValueError(f"Unknown algorithm: {algorithm}")
return {
"algorithm": algorithm,
"min_path_sum": result,
"time_seconds": duration
}
def verify(self, input_data: Any, result: Any) -> bool:
grid = input_data.get("grid", [])
truth, _ = time_execution(solve_dp)(grid)
return result.get("min_path_sum") == truth

View File

@ -0,0 +1,56 @@
from typing import List
def solve_dp(grid: List[List[int]]) -> int:
"""
Dynamic Programming solution for Minimum Path Sum.
O(M * N)
"""
if not grid or not grid[0]:
return 0
rows, cols = len(grid), len(grid[0])
dp = [[0] * cols for _ in range(rows)]
dp[0][0] = grid[0][0]
# Initialize first row
for j in range(1, cols):
dp[0][j] = dp[0][j-1] + grid[0][j]
# Initialize first column
for i in range(1, rows):
dp[i][0] = dp[i-1][0] + grid[i][0]
# Fill the rest
for i in range(1, rows):
for j in range(1, cols):
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
return dp[rows-1][cols-1]
def solve_recursion(grid: List[List[int]]) -> int:
"""
Recursive Brute Force solution (Top-Down without Memoization).
O(2^(M+N))
Only for very small inputs!
"""
if not grid or not grid[0]:
return 0
rows, cols = len(grid), len(grid[0])
# Safety check to prevent freezing/recursion depth errors on large inputs if called accidentally
if rows + cols > 18:
# Fallback to DP for safety if input is too large for naive recursion
return solve_dp(grid)
def helper(r, c):
if r == 0 and c == 0:
return grid[0][0]
if r < 0 or c < 0:
return float('inf')
return grid[r][c] + min(helper(r-1, c), helper(r, c-1))
return helper(rows-1, cols-1)

View File

@ -0,0 +1,75 @@
from fastapi import APIRouter
from pydantic import BaseModel
from typing import List, Any
from problems.advantage_shuffle.problem import AdvantageShuffleProblem
router = APIRouter(
prefix="/advantage_shuffle",
tags=["advantage_shuffle"],
responses={404: {"description": "Not found"}},
)
problem_solver = AdvantageShuffleProblem()
class GenerateRequest(BaseModel):
size: int = 10
max_val: int = 100
class SolveRequest(BaseModel):
nums1: List[int]
nums2: List[int]
algorithms: List[str] = ["greedy", "brute_force", "random"]
class BenchmarkRequest(BaseModel):
sizes: List[int] = [5, 8, 100, 1000]
algorithms: List[str] = ["greedy", "brute_force", "random"]
@router.post("/generate")
async def generate_case(req: GenerateRequest):
return problem_solver.generate_case(size=req.size, max_val=req.max_val)
@router.post("/solve")
async def solve_case(req: SolveRequest):
input_data = {"nums1": req.nums1, "nums2": req.nums2}
results = []
for algo in req.algorithms:
try:
if algo == "brute_force" and len(req.nums1) > 8:
results.append({
"algorithm": algo,
"error": "Input too large for Brute Force (limit 8)",
"skipped": True
})
continue
res = problem_solver.solve(input_data, algo)
results.append(res)
except Exception as e:
results.append({"algorithm": algo, "error": str(e)})
return results
@router.post("/benchmark")
async def benchmark(req: BenchmarkRequest):
benchmark_results = []
for size in req.sizes:
input_data = problem_solver.generate_case(size=size)
size_result = {"size": size, "algorithms": []}
for algo in req.algorithms:
try:
if algo == "brute_force" and size > 8:
size_result["algorithms"].append({
"algorithm": algo,
"time_seconds": None,
"skipped": True
})
continue
res = problem_solver.solve(input_data, algo)
size_result["algorithms"].append(res)
except Exception as e:
size_result["algorithms"].append({"algorithm": algo, "error": str(e)})
benchmark_results.append(size_result)
return benchmark_results

View File

@ -0,0 +1,75 @@
from fastapi import APIRouter
from pydantic import BaseModel
from typing import List, Any
from problems.flower_planting.problem import FlowerPlantingProblem
router = APIRouter(
prefix="/flower_planting",
tags=["flower_planting"],
responses={404: {"description": "Not found"}},
)
problem_solver = FlowerPlantingProblem()
class GenerateRequest(BaseModel):
length: int = 10
n: int = 0 # 0 means auto-generate challenging N
class SolveRequest(BaseModel):
flowerbed: List[int]
n: int
algorithms: List[str] = ["greedy", "brute_force"]
class BenchmarkRequest(BaseModel):
sizes: List[int] = [10, 15, 20, 25]
algorithms: List[str] = ["greedy", "brute_force"]
@router.post("/generate")
async def generate_case(req: GenerateRequest):
return problem_solver.generate_case(length=req.length, n=req.n)
@router.post("/solve")
async def solve_case(req: SolveRequest):
input_data = {"flowerbed": req.flowerbed, "n": req.n}
results = []
for algo in req.algorithms:
try:
if algo == "brute_force" and len(req.flowerbed) > 25:
results.append({
"algorithm": algo,
"error": "Input too large for Brute Force (limit 25)",
"skipped": True
})
continue
res = problem_solver.solve(input_data, algo)
results.append(res)
except Exception as e:
results.append({"algorithm": algo, "error": str(e)})
return results
@router.post("/benchmark")
async def benchmark(req: BenchmarkRequest):
benchmark_results = []
for size in req.sizes:
input_data = problem_solver.generate_case(length=size)
size_result = {"size": size, "algorithms": []}
for algo in req.algorithms:
try:
if algo == "brute_force" and size > 25:
size_result["algorithms"].append({
"algorithm": algo,
"time_seconds": None,
"skipped": True
})
continue
res = problem_solver.solve(input_data, algo)
size_result["algorithms"].append(res)
except Exception as e:
size_result["algorithms"].append({"algorithm": algo, "error": str(e)})
benchmark_results.append(size_result)
return benchmark_results

View File

@ -0,0 +1,82 @@
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import List, Optional, Dict, Any
from problems.min_path_sum.problem import MinPathSumProblem
router = APIRouter(
prefix="/min_path_sum",
tags=["min_path_sum"],
responses={404: {"description": "Not found"}},
)
problem_solver = MinPathSumProblem()
class GenerateRequest(BaseModel):
rows: int = 5
cols: int = 5
min_val: int = 1
max_val: int = 20
class SolveRequest(BaseModel):
grid: List[List[int]]
algorithms: List[str] = ["dp", "recursion"]
class BenchmarkRequest(BaseModel):
sizes: List[int] = [4, 6, 8, 10, 12] # Assuming square grids for simplicity in benchmark input, or handle appropriately
algorithms: List[str] = ["dp", "recursion"]
@router.post("/generate")
async def generate_case(req: GenerateRequest):
return problem_solver.generate_case(rows=req.rows, cols=req.cols, min_val=req.min_val, max_val=req.max_val)
@router.post("/solve")
async def solve_case(req: SolveRequest):
input_data = {"grid": req.grid}
results = []
# Check dimensions for safety check if needed
rows = len(req.grid)
cols = len(req.grid[0]) if rows > 0 else 0
for algo in req.algorithms:
try:
# Recursion safety limit
if algo == "recursion" and (rows + cols > 18):
results.append({
"algorithm": algo,
"error": "Input too large for Recursion (limit rows+cols <= 18)",
"skipped": True
})
continue
res = problem_solver.solve(input_data, algo)
results.append(res)
except ValueError as e:
results.append({"algorithm": algo, "error": str(e)})
return results
@router.post("/benchmark")
async def benchmark(req: BenchmarkRequest):
benchmark_results = []
for size in req.sizes:
# Use size as both rows and cols for benchmarking
input_data = problem_solver.generate_case(rows=size, cols=size)
size_result = {"size": size, "algorithms": []}
for algo in req.algorithms:
try:
if algo == "recursion" and (size + size > 18):
size_result["algorithms"].append({
"algorithm": algo,
"time_seconds": None,
"skipped": True
})
continue
res = problem_solver.solve(input_data, algo)
size_result["algorithms"].append(res)
except Exception as e:
size_result["algorithms"].append({"algorithm": algo, "error": str(e)})
benchmark_results.append(size_result)
return benchmark_results

View File

@ -3,6 +3,9 @@ import MaxSubarray from './components/MaxSubarray';
import IntervalScheduling from './components/IntervalScheduling';
import NQueens from './components/NQueens';
import Knapsack from './components/Knapsack';
import MinPathSum from './components/MinPathSum';
import FlowerPlanting from './components/FlowerPlanting';
import AdvantageShuffle from './components/AdvantageShuffle';
function App() {
const [activeTab, setActiveTab] = useState('max_subarray');
@ -51,6 +54,33 @@ function App() {
>
0/1 背包
</button>
<button
onClick={() => setActiveTab('min_path_sum')}
className={`px-3 py-2 rounded-md text-sm font-medium ${activeTab === 'min_path_sum'
? 'bg-blue-100 text-blue-700'
: 'text-gray-600 hover:bg-gray-100'
}`}
>
最小路径和
</button>
<button
onClick={() => setActiveTab('flower_planting')}
className={`px-3 py-2 rounded-md text-sm font-medium ${activeTab === 'flower_planting'
? 'bg-blue-100 text-blue-700'
: 'text-gray-600 hover:bg-gray-100'
}`}
>
种花问题
</button>
<button
onClick={() => setActiveTab('advantage_shuffle')}
className={`px-3 py-2 rounded-md text-sm font-medium ${activeTab === 'advantage_shuffle'
? 'bg-blue-100 text-blue-700'
: 'text-gray-600 hover:bg-gray-100'
}`}
>
田忌赛马
</button>
</div>
</div>
</div>
@ -63,8 +93,22 @@ function App() {
{activeTab === 'interval_scheduling' && <IntervalScheduling />}
{activeTab === 'n_queens' && <NQueens />}
{activeTab === 'knapsack' && <Knapsack />}
{activeTab === 'min_path_sum' && <MinPathSum />}
{activeTab === 'flower_planting' && <FlowerPlanting />}
{activeTab === 'advantage_shuffle' && <AdvantageShuffle />}
</div>
</main>
<footer className="bg-white shadow-inner py-6 mt-auto">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center text-gray-600">
<p>
<a href="mailto:i@hissin.net" className="text-blue-600 hover:underline">
韩天泽 (i@hissin.net)
</a>
{' '} | 电计4班 | 1120230489
</p>
</div>
</footer>
</div>
);
}

View File

@ -101,3 +101,82 @@ export const runKnapsackBenchmark = async (sizes, algorithms) => {
});
return response.data;
};
// Min Path Sum API
export const generateMinPathSumCase = async (rows, cols, minVal, maxVal) => {
const response = await axios.post(`${API_URL}/min_path_sum/generate`, {
rows,
cols,
min_val: minVal,
max_val: maxVal
});
return response.data;
};
export const solveMinPathSumCase = async (grid, algorithms) => {
const response = await axios.post(`${API_URL}/min_path_sum/solve`, {
grid,
algorithms
});
return response.data;
};
export const runMinPathSumBenchmark = async (sizes, algorithms) => {
const response = await axios.post(`${API_URL}/min_path_sum/benchmark`, {
sizes,
algorithms
});
return response.data;
};
// Flower Planting API
export const generateFlowerCase = async (length, n) => {
const response = await axios.post(`${API_URL}/flower_planting/generate`, {
length,
n
});
return response.data;
};
export const solveFlowerCase = async (flowerbed, n, algorithms) => {
const response = await axios.post(`${API_URL}/flower_planting/solve`, {
flowerbed,
n,
algorithms
});
return response.data;
};
export const runFlowerBenchmark = async (sizes, algorithms) => {
const response = await axios.post(`${API_URL}/flower_planting/benchmark`, {
sizes,
algorithms
});
return response.data;
};
// Advantage Shuffle API
export const generateAdvantageCase = async (size, maxVal) => {
const response = await axios.post(`${API_URL}/advantage_shuffle/generate`, {
size,
max_val: maxVal
});
return response.data;
};
export const solveAdvantageCase = async (nums1, nums2, algorithms) => {
const response = await axios.post(`${API_URL}/advantage_shuffle/solve`, {
nums1,
nums2,
algorithms
});
return response.data;
};
export const runAdvantageBenchmark = async (sizes, algorithms) => {
const response = await axios.post(`${API_URL}/advantage_shuffle/benchmark`, {
sizes,
algorithms
});
return response.data;
};

View File

@ -0,0 +1,244 @@
import React, { useState } from 'react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
import { generateAdvantageCase, solveAdvantageCase, runAdvantageBenchmark } from '../api';
const AdvantageShuffle = () => {
const [params, setParams] = useState({ size: 10, max: 100 });
const [currentCase, setCurrentCase] = useState(null);
const [solveResults, setSolveResults] = useState(null);
const [benchmarkResults, setBenchmarkResults] = useState(null);
const [loading, setLoading] = useState(false);
const [selectedAlgorithms, setSelectedAlgorithms] = useState({
greedy: true,
random: true,
brute_force: true
});
const algorithmsList = [
{ id: 'greedy', name: '田忌赛马策略 (Greedy) O(N log N)' },
{ id: 'random', name: '随机洗牌 (Random)' },
{ id: 'brute_force', name: '暴力全排列 (Brute Force) O(N!)' }
];
const getSelectedAlgoKeys = () => {
return Object.keys(selectedAlgorithms).filter(k => selectedAlgorithms[k]);
};
const handleGenerate = async () => {
setLoading(true);
try {
const data = await generateAdvantageCase(params.size, params.max);
setCurrentCase(data);
setSolveResults(null);
} catch (error) {
console.error("Error generating case:", error);
alert("生成测试用例失败");
}
setLoading(false);
};
const handleSolve = async () => {
if (!currentCase) return;
const algos = getSelectedAlgoKeys();
if (algos.length === 0) {
alert("请至少选择一种算法");
return;
}
setLoading(true);
try {
const results = await solveAdvantageCase(currentCase.nums1, currentCase.nums2, algos);
setSolveResults(results);
} catch (error) {
console.error("Error solving case:", error);
alert("求解失败");
}
setLoading(false);
};
const handleBenchmark = async () => {
const algos = getSelectedAlgoKeys();
if (algos.length === 0) {
alert("请至少选择一种算法");
return;
}
setLoading(true);
try {
const sizes = [5, 7, 9, 11, 100, 500];
const results = await runAdvantageBenchmark(sizes, algos);
const chartData = results.map(item => {
const point = { size: item.size };
item.algorithms.forEach(algo => {
if (algo.time_seconds !== undefined && algo.time_seconds !== null) {
point[algo.algorithm] = algo.time_seconds;
}
});
return point;
});
setBenchmarkResults(chartData);
} catch (error) {
console.error("Error running benchmark:", error);
alert("性能测试失败");
}
setLoading(false);
};
const toggleAlgorithm = (algoId) => {
setSelectedAlgorithms(prev => ({
...prev,
[algoId]: !prev[algoId]
}));
};
return (
<div className="p-6 space-y-8">
<h1 className="text-3xl font-bold text-gray-800">优势洗牌 (田忌赛马)</h1>
<p className="text-gray-600">目标给定两个数组 A B重新排列 A使得 A[i] {'>'} B[i] 的索引数目最大化</p>
{/* Algorithm Selection */}
<div className="bg-white p-6 rounded-lg shadow">
<h2 className="text-xl font-semibold mb-4">算法选择</h2>
<div className="flex flex-wrap gap-4">
{algorithmsList.map(algo => (
<label key={algo.id} className="flex items-center space-x-2 cursor-pointer">
<input
type="checkbox"
checked={selectedAlgorithms[algo.id]}
onChange={() => toggleAlgorithm(algo.id)}
className="form-checkbox h-5 w-5 text-blue-600"
/>
<span className="text-gray-700">{algo.name}</span>
</label>
))}
</div>
</div>
{/* Section 1: Generate & Solve */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="bg-white p-6 rounded-lg shadow">
<h2 className="text-xl font-semibold mb-4">1. 生成测试用例</h2>
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700">数组长度 (N)</label>
<input
type="number"
value={params.size}
onChange={(e) => setParams({ ...params, size: parseInt(e.target.value) })}
className="mt-1 block w-full border rounded-md p-2"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">最大数值</label>
<input
type="number"
value={params.max}
onChange={(e) => setParams({ ...params, max: parseInt(e.target.value) })}
className="mt-1 block w-full border rounded-md p-2"
/>
</div>
</div>
<button
onClick={handleGenerate}
disabled={loading}
className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:bg-gray-400"
>
{loading ? '处理中...' : '生成测试用例'}
</button>
</div>
{currentCase && (
<div className="mt-4 space-y-2">
<div>
<h3 className="font-medium">我方马匹 (A):</h3>
<div className="font-mono text-sm bg-blue-50 p-2 rounded overflow-x-auto">
{currentCase.nums1.join(', ')}
</div>
</div>
<div>
<h3 className="font-medium">敌方马匹 (B):</h3>
<div className="font-mono text-sm bg-red-50 p-2 rounded overflow-x-auto">
{currentCase.nums2.join(', ')}
</div>
</div>
</div>
)}
</div>
<div className="bg-white p-6 rounded-lg shadow">
<h2 className="text-xl font-semibold mb-4">2. 求解与验证</h2>
<button
onClick={handleSolve}
disabled={!currentCase || loading}
className="w-full bg-green-600 text-white py-2 px-4 rounded-md hover:bg-green-700 disabled:bg-gray-400 mb-4"
>
运行选中算法
</button>
{solveResults && (
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">算法</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">胜场数 (Advantage)</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">耗时 ()</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{solveResults.map((res, idx) => (
<tr key={idx}>
<td className="px-4 py-2 text-sm font-medium text-gray-900">{res.algorithm}</td>
<td className="px-4 py-2 text-sm text-gray-500 font-bold">{res.advantage_score}</td>
<td className="px-4 py-2 text-sm text-gray-500">{res.time_seconds?.toFixed(6)}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
{/* Section 2: Benchmark */}
<div className="bg-white p-6 rounded-lg shadow">
<div className="flex justify-between items-center mb-6">
<h2 className="text-xl font-semibold">3. 性能对比测试</h2>
<button
onClick={handleBenchmark}
disabled={loading}
className="bg-purple-600 text-white py-2 px-4 rounded-md hover:bg-purple-700 disabled:bg-gray-400"
>
运行性能测试
</button>
</div>
{benchmarkResults && (
<div className="h-96 w-full">
<ResponsiveContainer width="100%" height="100%">
<LineChart
data={benchmarkResults}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="size" label={{ value: '数组大小 (N)', position: 'insideBottomRight', offset: -10 }} />
<YAxis label={{ value: '耗时 (秒)', angle: -90, position: 'insideLeft' }} />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="greedy" stroke="#00ff00" name="田忌赛马(Greedy)" />
<Line type="monotone" dataKey="random" stroke="#0000ff" name="随机乱序(Random)" />
<Line type="monotone" dataKey="brute_force" stroke="#ff0000" name="暴力(Brute Force)" />
</LineChart>
</ResponsiveContainer>
</div>
)}
</div>
</div>
);
};
export default AdvantageShuffle;

View File

@ -0,0 +1,239 @@
import React, { useState } from 'react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
import { generateFlowerCase, solveFlowerCase, runFlowerBenchmark } from '../api';
const FlowerPlanting = () => {
const [params, setParams] = useState({ length: 10, n: 0 });
const [currentCase, setCurrentCase] = useState(null);
const [currentN, setCurrentN] = useState(0);
const [solveResults, setSolveResults] = useState(null);
const [benchmarkResults, setBenchmarkResults] = useState(null);
const [loading, setLoading] = useState(false);
const [selectedAlgorithms, setSelectedAlgorithms] = useState({
greedy: true,
brute_force: true
});
const algorithmsList = [
{ id: 'greedy', name: '贪心算法 (Greedy) O(N)' },
{ id: 'brute_force', name: '回溯暴力 (Brute Force) O(2^N)' }
];
const getSelectedAlgoKeys = () => {
return Object.keys(selectedAlgorithms).filter(k => selectedAlgorithms[k]);
};
const handleGenerate = async () => {
setLoading(true);
try {
const data = await generateFlowerCase(params.length, params.n);
setCurrentCase(data.flowerbed);
setCurrentN(data.n);
setSolveResults(null);
} catch (error) {
console.error("Error generating case:", error);
alert("生成测试用例失败");
}
setLoading(false);
};
const handleSolve = async () => {
if (!currentCase) return;
const algos = getSelectedAlgoKeys();
if (algos.length === 0) {
alert("请至少选择一种算法");
return;
}
setLoading(true);
try {
const results = await solveFlowerCase(currentCase, currentN, algos);
setSolveResults(results);
} catch (error) {
console.error("Error solving case:", error);
alert("求解失败");
}
setLoading(false);
};
const handleBenchmark = async () => {
const algos = getSelectedAlgoKeys();
if (algos.length === 0) {
alert("请至少选择一种算法");
return;
}
setLoading(true);
try {
const sizes = [10, 15, 20, 25, 28];
const results = await runFlowerBenchmark(sizes, algos);
const chartData = results.map(item => {
const point = { size: item.size };
item.algorithms.forEach(algo => {
if (algo.time_seconds !== undefined && algo.time_seconds !== null) {
point[algo.algorithm] = algo.time_seconds;
}
});
return point;
});
setBenchmarkResults(chartData);
} catch (error) {
console.error("Error running benchmark:", error);
alert("性能测试失败");
}
setLoading(false);
};
const toggleAlgorithm = (algoId) => {
setSelectedAlgorithms(prev => ({
...prev,
[algoId]: !prev[algoId]
}));
};
return (
<div className="p-6 space-y-8">
<h1 className="text-3xl font-bold text-gray-800">种花问题 (Flower Planting)</h1>
<p className="text-gray-600">目标判断在不打破种植规则花不能相邻的情况下能否在花坛中再种下 N 朵花</p>
{/* Algorithm Selection */}
<div className="bg-white p-6 rounded-lg shadow">
<h2 className="text-xl font-semibold mb-4">算法选择</h2>
<div className="flex flex-wrap gap-4">
{algorithmsList.map(algo => (
<label key={algo.id} className="flex items-center space-x-2 cursor-pointer">
<input
type="checkbox"
checked={selectedAlgorithms[algo.id]}
onChange={() => toggleAlgorithm(algo.id)}
className="form-checkbox h-5 w-5 text-blue-600"
/>
<span className="text-gray-700">{algo.name}</span>
</label>
))}
</div>
</div>
{/* Section 1: Generate & Solve */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="bg-white p-6 rounded-lg shadow">
<h2 className="text-xl font-semibold mb-4">1. 生成测试用例</h2>
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700">花坛长度</label>
<input
type="number"
value={params.length}
onChange={(e) => setParams({ ...params, length: parseInt(e.target.value) })}
className="mt-1 block w-full border rounded-md p-2"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">目标 N (0=随机)</label>
<input
type="number"
value={params.n}
onChange={(e) => setParams({ ...params, n: parseInt(e.target.value) })}
className="mt-1 block w-full border rounded-md p-2"
/>
</div>
</div>
<button
onClick={handleGenerate}
disabled={loading}
className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:bg-gray-400"
>
{loading ? '处理中...' : '生成测试用例'}
</button>
</div>
{currentCase && (
<div className="mt-4">
<h3 className="font-medium mb-2">花坛状态 (N={currentN}):</h3>
<div className="font-mono bg-gray-100 p-2 rounded break-all text-sm">
{currentCase.map(x => x === 1 ? '🌸' : '🌱').join('')}
</div>
<p className="text-xs text-gray-500 mt-1">长度: {currentCase.length}</p>
</div>
)}
</div>
<div className="bg-white p-6 rounded-lg shadow">
<h2 className="text-xl font-semibold mb-4">2. 求解与验证</h2>
<button
onClick={handleSolve}
disabled={!currentCase || loading}
className="w-full bg-green-600 text-white py-2 px-4 rounded-md hover:bg-green-700 disabled:bg-gray-400 mb-4"
>
运行选中算法
</button>
{solveResults && (
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">算法</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">能否种下?</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">耗时 ()</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{solveResults.map((res, idx) => (
<tr key={idx}>
<td className="px-4 py-2 text-sm font-medium text-gray-900">{res.algorithm}</td>
<td className="px-4 py-2 text-sm text-gray-500">
{res.can_plant === true ? <span className="text-green-600 font-bold"></span> :
res.can_plant === false ? <span className="text-red-600 font-bold"></span> : '-'}
</td>
<td className="px-4 py-2 text-sm text-gray-500">{res.time_seconds?.toFixed(6)}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
{/* Section 2: Benchmark */}
<div className="bg-white p-6 rounded-lg shadow">
<div className="flex justify-between items-center mb-6">
<h2 className="text-xl font-semibold">3. 性能对比测试</h2>
<button
onClick={handleBenchmark}
disabled={loading}
className="bg-purple-600 text-white py-2 px-4 rounded-md hover:bg-purple-700 disabled:bg-gray-400"
>
运行性能测试
</button>
</div>
{benchmarkResults && (
<div className="h-96 w-full">
<ResponsiveContainer width="100%" height="100%">
<LineChart
data={benchmarkResults}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="size" label={{ value: '花坛长度', position: 'insideBottomRight', offset: -10 }} />
<YAxis label={{ value: '耗时 (秒)', angle: -90, position: 'insideLeft' }} />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="greedy" stroke="#00ff00" name="贪心算法" />
<Line type="monotone" dataKey="brute_force" stroke="#ff0000" name="暴力回溯" />
</LineChart>
</ResponsiveContainer>
</div>
)}
</div>
</div>
);
};
export default FlowerPlanting;

View File

@ -0,0 +1,267 @@
import React, { useState } from 'react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
import { generateMinPathSumCase, solveMinPathSumCase, runMinPathSumBenchmark } from '../api';
const MinPathSum = () => {
const [params, setParams] = useState({ rows: 5, cols: 5, min: 1, max: 20 });
const [currentCase, setCurrentCase] = useState(null);
const [solveResults, setSolveResults] = useState(null);
const [benchmarkResults, setBenchmarkResults] = useState(null);
const [loading, setLoading] = useState(false);
const [selectedAlgorithms, setSelectedAlgorithms] = useState({
dp: true,
recursion: true
});
const algorithmsList = [
{ id: 'dp', name: '动态规划 (DP) O(MN)' },
{ id: 'recursion', name: '递归 (Recursion) O(2^(M+N))' }
];
const getSelectedAlgoKeys = () => {
return Object.keys(selectedAlgorithms).filter(k => selectedAlgorithms[k]);
};
const handleGenerate = async () => {
setLoading(true);
try {
const data = await generateMinPathSumCase(params.rows, params.cols, params.min, params.max);
setCurrentCase(data.grid);
setSolveResults(null);
} catch (error) {
console.error("Error generating case:", error);
alert("生成测试用例失败");
}
setLoading(false);
};
const handleSolve = async () => {
if (!currentCase) return;
const algos = getSelectedAlgoKeys();
if (algos.length === 0) {
alert("请至少选择一种算法");
return;
}
setLoading(true);
try {
const results = await solveMinPathSumCase(currentCase, algos);
setSolveResults(results);
} catch (error) {
console.error("Error solving case:", error);
alert("求解失败");
}
setLoading(false);
};
const handleBenchmark = async () => {
const algos = getSelectedAlgoKeys();
if (algos.length === 0) {
alert("请至少选择一种算法");
return;
}
setLoading(true);
try {
// Sizes for Square grids (N x N)
const sizes = [2, 4, 6, 8, 10, 12, 14];
// Recursion will be very slow around 15x15 (2^30 operations roughly is too big, but O(2^(M+N)) is loose bound.
// Actually C(M+N-2, N-1) paths. C(28, 14) = 40 million calls. Too slow for web.
// Stick to small sizes.
const results = await runMinPathSumBenchmark(sizes, algos);
const chartData = results.map(item => {
const point = { size: item.size };
item.algorithms.forEach(algo => {
if (algo.time_seconds !== undefined && algo.time_seconds !== null) {
point[algo.algorithm] = algo.time_seconds;
}
});
return point;
});
setBenchmarkResults(chartData);
} catch (error) {
console.error("Error running benchmark:", error);
alert("性能测试失败");
}
setLoading(false);
};
const toggleAlgorithm = (algoId) => {
setSelectedAlgorithms(prev => ({
...prev,
[algoId]: !prev[algoId]
}));
};
return (
<div className="p-6 space-y-8">
<h1 className="text-3xl font-bold text-gray-800">最小路径和 (Minimum Path Sum)</h1>
<p className="text-gray-600">目标寻找从网格左上角到右下角的路径使得路径上的数字总和最小每次只能向下或向右移动</p>
{/* Algorithm Selection */}
<div className="bg-white p-6 rounded-lg shadow">
<h2 className="text-xl font-semibold mb-4">算法选择</h2>
<div className="flex flex-wrap gap-4">
{algorithmsList.map(algo => (
<label key={algo.id} className="flex items-center space-x-2 cursor-pointer">
<input
type="checkbox"
checked={selectedAlgorithms[algo.id]}
onChange={() => toggleAlgorithm(algo.id)}
className="form-checkbox h-5 w-5 text-blue-600"
/>
<span className="text-gray-700">{algo.name}</span>
</label>
))}
</div>
</div>
{/* Section 1: Generate & Solve */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="bg-white p-6 rounded-lg shadow">
<h2 className="text-xl font-semibold mb-4">1. 生成测试用例</h2>
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700">行数 (Rows)</label>
<input
type="number"
value={params.rows}
onChange={(e) => setParams({ ...params, rows: parseInt(e.target.value) })}
className="mt-1 block w-full border rounded-md p-2"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">列数 (Cols)</label>
<input
type="number"
value={params.cols}
onChange={(e) => setParams({ ...params, cols: parseInt(e.target.value) })}
className="mt-1 block w-full border rounded-md p-2"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">最小值</label>
<input
type="number"
value={params.min}
onChange={(e) => setParams({ ...params, min: parseInt(e.target.value) })}
className="mt-1 block w-full border rounded-md p-2"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700">最大值</label>
<input
type="number"
value={params.max}
onChange={(e) => setParams({ ...params, max: parseInt(e.target.value) })}
className="mt-1 block w-full border rounded-md p-2"
/>
</div>
</div>
<button
onClick={handleGenerate}
disabled={loading}
className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:bg-gray-400"
>
{loading ? '处理中...' : '生成测试用例'}
</button>
</div>
{currentCase && (
<div className="mt-4">
<h3 className="font-medium mb-2">网格预览:</h3>
<div className="overflow-auto max-h-64 border rounded p-2 bg-gray-50">
<table className="table-auto border-collapse w-full text-center text-sm">
<tbody>
{currentCase.map((row, rIdx) => (
<tr key={rIdx}>
{row.map((val, cIdx) => (
<td key={cIdx} className="border px-2 py-1">{val}</td>
))}
</tr>
))}
</tbody>
</table>
</div>
<p className="text-xs text-gray-500 mt-1">规模: {currentCase.length} x {currentCase[0].length}</p>
</div>
)}
</div>
<div className="bg-white p-6 rounded-lg shadow">
<h2 className="text-xl font-semibold mb-4">2. 求解与验证</h2>
<button
onClick={handleSolve}
disabled={!currentCase || loading}
className="w-full bg-green-600 text-white py-2 px-4 rounded-md hover:bg-green-700 disabled:bg-gray-400 mb-4"
>
运行选中算法
</button>
{solveResults && (
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">算法</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">最小路径和</th>
<th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">耗时 ()</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{solveResults.map((res, idx) => (
<tr key={idx}>
<td className="px-4 py-2 text-sm font-medium text-gray-900">{res.algorithm}</td>
<td className="px-4 py-2 text-sm text-gray-500">{res.min_path_sum}</td>
<td className="px-4 py-2 text-sm text-gray-500">{res.time_seconds?.toFixed(6)}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
{/* Section 2: Benchmark */}
<div className="bg-white p-6 rounded-lg shadow">
<div className="flex justify-between items-center mb-6">
<h2 className="text-xl font-semibold">3. 性能对比测试</h2>
<button
onClick={handleBenchmark}
disabled={loading}
className="bg-purple-600 text-white py-2 px-4 rounded-md hover:bg-purple-700 disabled:bg-gray-400"
>
运行性能测试
</button>
</div>
{benchmarkResults && (
<div className="h-96 w-full">
<ResponsiveContainer width="100%" height="100%">
<LineChart
data={benchmarkResults}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="size" label={{ value: '网格大小 (N x N)', position: 'insideBottomRight', offset: -10 }} />
<YAxis label={{ value: '耗时 (秒)', angle: -90, position: 'insideLeft' }} />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="dp" stroke="#00ff00" name="动态规划" />
<Line type="monotone" dataKey="recursion" stroke="#ff0000" name="递归" />
</LineChart>
</ResponsiveContainer>
</div>
)}
</div>
</div>
);
};
export default MinPathSum;