HARMONYOS应用实例274:七桥问题探究
·
- 七桥问题探究
- 功能:模拟哥尼斯堡七桥问题,用户尝试遍历所有桥而不重复,引出“一笔画”定理。
七桥问题应用通过模拟哥尼斯堡七桥问题,让用户尝试遍历所有桥而不重复,引出"一笔画"定理。哥尼斯堡七桥问题是图论的经典问题,由数学家欧拉在1736年解决。这个问题引出了图论和拓扑学的重要概念,是"一笔画"定理的基础。本应用通过交互式方式让用户尝试遍历七座桥,理解欧拉定理。
- 功能:模拟哥尼斯堡七桥问题,用户尝试遍历所有桥而不重复,引出“一笔画”定理。
// 七桥问题探究
// 功能:模拟哥尼斯堡七桥问题,用户尝试遍历所有桥而不重复,引出"一笔画"定理。
// 哥尼斯堡七桥问题是图论的经典问题,由数学家欧拉在1736年解决。
// 这个问题引出了图论和拓扑学的重要概念,是"一笔画"定理的基础。
// 本应用通过交互式方式让用户尝试遍历七座桥,理解欧拉定理。
// 地点接口
interface LandPoint {
id: string;
x: number;
y: number;
name: string;
degree: number;
}
// 桥接口
interface Bridge {
id: string;
start: string;
end: string;
used: boolean;
}
// 路径点接口
interface PathPoint {
x: number;
y: number;
isLand: boolean;
}
@Entry
@Component
struct SevenBridgesProblem {
@State selectedLand: string = '';
@State path: PathPoint[] = [];
@State bridgesUsed: number = 0;
@State totalBridges: number = 7;
@State showSolution: boolean = false;
@State showEulerPath: boolean = false;
@State canvasWidth: number = 350;
@State canvasHeight: number = 300;
@State isComplete: boolean = false;
@State message: string = '';
private canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D();
private landPoints: LandPoint[] = [
{ id: 'A', x: 120, y: 80, name: '北岸', degree: 3 },
{ id: 'B', x: 230, y: 80, name: '岛', degree: 5 },
{ id: 'C', x: 120, y: 180, name: '南岸', degree: 3 },
{ id: 'D', x: 230, y: 180, name: '东岸', degree: 3 }
];
private bridges: Bridge[] = [
{ id: '1', start: 'A', end: 'B', used: false },
{ id: '2', start: 'A', end: 'B', used: false },
{ id: '3', start: 'A', end: 'C', used: false },
{ id: '4', start: 'B', end: 'C', used: false },
{ id: '5', start: 'B', end: 'C', used: false },
{ id: '6', start: 'B', end: 'D', used: false },
{ id: '7', start: 'C', end: 'D', used: false }
];
private drawLand(ctx: CanvasRenderingContext2D, point: LandPoint) {
const radius = 25;
ctx.fillStyle = '#2196F3';
ctx.beginPath();
ctx.arc(point.x, point.y, radius, 0, 2 * Math.PI);
ctx.fill();
ctx.strokeStyle = '#1976D2';
ctx.lineWidth = 3;
ctx.stroke();
ctx.fillStyle = '#FFFFFF';
ctx.font = 'bold 16px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(point.id, point.x, point.y);
ctx.fillStyle = '#333';
ctx.font = '12px sans-serif';
ctx.fillText(point.name, point.x, point.y + radius + 15);
}
private drawBridge(ctx: CanvasRenderingContext2D, bridge: Bridge, start: LandPoint, end: LandPoint) {
ctx.strokeStyle = bridge.used ? '#4CAF50' : '#E0E0E0';
ctx.lineWidth = bridge.used ? 4 : 2;
ctx.setLineDash(bridge.used ? [] : [5, 3]);
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(end.x, end.y);
ctx.stroke();
ctx.setLineDash([]);
if (!bridge.used) {
ctx.fillStyle = '#E0E0E0';
ctx.font = '10px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const midX = (start.x + end.x) / 2;
const midY = (start.y + end.y) / 2;
ctx.fillText(bridge.id, midX, midY);
}
}
private drawPath(ctx: CanvasRenderingContext2D) {
if (this.path.length < 2) return;
ctx.strokeStyle = '#E91E63';
ctx.lineWidth = 3;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.beginPath();
ctx.moveTo(this.path[0].x, this.path[0].y);
for (let i = 1; i < this.path.length; i++) {
ctx.lineTo(this.path[i].x, this.path[i].y);
}
ctx.stroke();
for (let i = 0; i < this.path.length; i++) {
const point = this.path[i];
ctx.fillStyle = point.isLand ? '#E91E63' : '#FFFFFF';
ctx.beginPath();
ctx.arc(point.x, point.y, 5, 0, 2 * Math.PI);
ctx.fill();
}
}
private drawCanvas() {
const ctx = this.canvasContext;
const width = this.canvasWidth;
const height = this.canvasHeight;
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = '#FAFAFA';
ctx.fillRect(0, 0, width, height);
this.drawBridges(ctx);
this.drawLands(ctx);
this.drawPath(ctx);
this.drawGrid(ctx);
}
private drawBridges(ctx: CanvasRenderingContext2D) {
this.bridges.forEach(bridge => {
const start = this.landPoints.find(p => p.id === bridge.start);
const end = this.landPoints.find(p => p.id === bridge.end);
if (start && end) {
this.drawBridge(ctx, bridge, start, end);
}
});
}
private drawLands(ctx: CanvasRenderingContext2D) {
this.landPoints.forEach(point => {
this.drawLand(ctx, point);
});
}
private drawGrid(ctx: CanvasRenderingContext2D) {
const width = this.canvasWidth;
const height = this.canvasHeight;
ctx.strokeStyle = '#E0E0E0';
ctx.lineWidth = 1;
for (let x = 0; x <= width; x += 50) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, height);
ctx.stroke();
}
for (let y = 0; y <= height; y += 50) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(width, y);
ctx.stroke();
}
}
private findBridge(startId: string, endId: string): Bridge | undefined {
return this.bridges.find(bridge => {
return (bridge.start === startId && bridge.end === endId) ||
(bridge.start === endId && bridge.end === startId);
});
}
private handleLandClick(landId: string) {
if (this.isComplete) return;
if (this.selectedLand === '') {
this.selectedLand = landId;
const land = this.landPoints.find(p => p.id === landId);
if (land) {
this.path.push({ x: land.x, y: land.y, isLand: true });
}
} else {
const bridge = this.findBridge(this.selectedLand, landId);
if (bridge && !bridge.used) {
bridge.used = true;
this.bridgesUsed++;
this.selectedLand = landId;
const land = this.landPoints.find(p => p.id === landId);
if (land) {
this.path.push({ x: land.x, y: land.y, isLand: true });
}
this.checkCompletion();
} else {
this.message = '这两地之间没有可用的桥!';
}
}
this.drawCanvas();
}
private checkCompletion() {
if (this.bridgesUsed === this.totalBridges) {
this.isComplete = true;
this.message = '恭喜!你成功遍历了所有桥!';
} else {
this.message = '';
}
}
private reset() {
this.selectedLand = '';
this.path = [];
this.bridgesUsed = 0;
this.isComplete = false;
this.message = '';
this.bridges.forEach(bridge => {
bridge.used = false;
});
this.drawCanvas();
}
private showEulerTheorem() {
this.showSolution = true;
this.message = '根据欧拉定理,哥尼斯堡七桥问题无解。';
}
private calculateOddDegreeNodes(): number {
return this.landPoints.filter(point => point.degree % 2 !== 0).length;
}
build() {
Column({ space: 15 }) {
Text('七桥问题探究')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#2196F3')
.margin({ top: 10 })
Text('模拟哥尼斯堡七桥问题,用户尝试遍历所有桥而不重复,引出"一笔画"定理。哥尼斯堡七桥问题是图论的经典问题,由数学家欧拉在1736年解决。')
.fontSize(14)
.fontColor('#666')
.textAlign(TextAlign.Center)
.padding({ left: 15, right: 15 })
Scroll() {
Column({ space: 15 }) {
Column({ space: 10 }) {
Text('哥尼斯堡地图')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333')
.width('100%')
Canvas(this.canvasContext)
.width(this.canvasWidth)
.height(this.canvasHeight)
.backgroundColor('#FFFFFF')
.border({ width: 2, color: '#333' })
.borderRadius(4)
.onReady(() => {
this.drawCanvas();
})
.gesture(
TapGesture()
.onAction((event: GestureEvent) => {
const x = event.fingerList[0].localX;
const y = event.fingerList[0].localY;
this.landPoints.forEach(point => {
const dx = x - point.x;
const dy = y - point.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 30) {
this.handleLandClick(point.id);
}
});
})
)
}
.width('100%')
.padding(15)
.backgroundColor('#FAFAFA')
.borderRadius(10)
if (this.message !== '') {
Column({ space: 5 }) {
Text(this.message)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(this.isComplete ? '#4CAF50' : '#FF5722')
.textAlign(TextAlign.Center)
.width('100%')
}
.width('100%')
.padding(10)
.backgroundColor(this.isComplete ? '#E8F5E9' : '#FFEBEE')
.borderRadius(8)
}
Column({ space: 10 }) {
Text('当前状态')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333')
.width('100%')
Row({ space: 10 }) {
Text('已用桥:')
.fontSize(14)
.fontColor('#333')
.width(80)
Text(`${this.bridgesUsed}/${this.totalBridges}`)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#2196F3')
.layoutWeight(1)
}
.width('100%')
Row({ space: 10 }) {
Text('奇数度节点:')
.fontSize(14)
.fontColor('#333')
.width(80)
Text(this.calculateOddDegreeNodes().toString())
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#E91E63')
.layoutWeight(1)
}
.width('100%')
}
.width('100%')
.padding(15)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Row({ space: 10 }) {
Button('重置')
.width('50%')
.height(50)
.backgroundColor('#FF9800')
.fontSize(16)
.onClick(() => {
this.reset();
})
Button('欧拉定理')
.width('50%')
.height(50)
.backgroundColor('#2196F3')
.fontSize(16)
.onClick(() => {
this.showEulerTheorem();
})
}
.width('100%')
if (this.showSolution) {
Column({ space: 10 }) {
Text('欧拉一笔画定理')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333')
.width('100%')
Column({ space: 5 }) {
Text('• 一个图可以一笔画完成,当且仅当它最多有两个奇数度节点')
.fontSize(14)
.fontColor('#666')
.width('100%')
Text('• 哥尼斯堡七桥问题有4个奇数度节点(北岸3、岛5、南岸3、东岸3)')
.fontSize(14)
.fontColor('#666')
.width('100%')
Text('• 因此,哥尼斯堡七桥问题无解,无法不重复地遍历所有桥')
.fontSize(14)
.fontColor('#666')
.width('100%')
Text('• 欧拉的解决开创了图论和拓扑学的新领域')
.fontSize(14)
.fontColor('#666')
.width('100%')
}
.width('100%')
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
}
.width('100%')
.padding(15)
.backgroundColor('#FAFAFA')
.borderRadius(10)
}
Column({ space: 10 }) {
Text('操作说明')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333')
.width('100%')
Column({ space: 5 }) {
Text('1. 点击一个地点(A、B、C、D)作为起点')
.fontSize(14)
.fontColor('#666')
.width('100%')
Text('2. 点击另一个地点,选择一座桥移动')
.fontSize(14)
.fontColor('#666')
.width('100%')
Text('3. 继续选择地点,尝试遍历所有7座桥')
.fontSize(14)
.fontColor('#666')
.width('100%')
Text('4. 每座桥只能使用一次,不能重复')
.fontSize(14)
.fontColor('#666')
.width('100%')
}
.width('100%')
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
}
.width('100%')
.padding(15)
.backgroundColor('#FAFAFA')
.borderRadius(10)
Column({ space: 5 }) {
Text('历史意义')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#2196F3')
Text('• 1736年,欧拉解决了哥尼斯堡七桥问题,这是图论的第一个问题')
.fontSize(12)
.fontColor('#666')
.width('100%')
Text('• 欧拉的解决方法开创了拓扑学和图论的新数学领域')
.fontSize(12)
.fontColor('#666')
.width('100%')
Text('• 一笔画定理至今仍是图论和算法设计中的重要工具')
.fontSize(12)
.fontColor('#666')
.width('100%')
Text('• 哥尼斯堡七桥问题展示了数学建模和抽象思维的重要性')
.fontSize(12)
.fontColor('#666')
.width('100%')
}
.width('100%')
.padding(15)
.backgroundColor('#E3F2FD')
.borderRadius(10)
}
.width('100%')
.padding(10)
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#FFFFFF')
}
}
更多推荐


所有评论(0)