1. 七桥问题探究
    • 功能:模拟哥尼斯堡七桥问题,用户尝试遍历所有桥而不重复,引出“一笔画”定理。
      七桥问题应用通过模拟哥尼斯堡七桥问题,让用户尝试遍历所有桥而不重复,引出"一笔画"定理。哥尼斯堡七桥问题是图论的经典问题,由数学家欧拉在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')
  }
}
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐