最短路算法——差分约束
差分约束
(1) 求不等式组的可行解
- 源点:从源点出发,一定可以走到所有的边
- 求可行解步骤:
- 先将每个不等式 x i ≤ x j + c x_i \le x_j + c xi≤xj+c,转化成一条从 s j s_j sj走到 s i s_i si,长度为 c k c_k ck 的一条边
- 找一个超级源点,使得该源点一定可以遍历到所有边
- 从源点求一遍单源最短路
- 结果一:如果存在负环,则原不等式组一定无解
- 结果二:如果没有负环,则 d i s t [ i ] dist[i] dist[i]就是原不等式组的一个可行解
(2) 如何求最大值或最小值
- 结论: 如果求的是最小值,则应该求最长路;如果求的是最大值,则应该求最短路;
- 问题1:如何转化 x i ≤ c x_i \le c xi≤c,其中c是一个常数
- 方法:建立一个超级源点,0,然后建立0->i,长度是c的边即可。
- 以求 x i x_i xi的最大值为例:求所有从 x i x_i xi出发,构成的不等式链 x i ≤ x j + c j ≤ x k + c 2 + c 1 ≤ . . . ≤ c 1 + c 2 + . . . x_i \le x_j+c_j \le x_k + c_2 + c_1 \le ...\le c_1+c_2+... xi≤xj+cj≤xk+c2+c1≤...≤c1+c2+...所计算出的上界(而这个上界要让所有关系都成立,那么就必须以最小的上界为上界,因此需要用最短路算法求到i这个点的最短距离)
(3) 关系:
每一个差分约束的问题都可以转换成最短路的问题
理论理解
题单
1. 糖果
第一眼:
- 感觉和floyd的排序那一道题有点相似之处,两个点之间都有关系(ps:关系闭包)
- 和排序不一样的地方在于,这道题确定小朋友各种胡搅蛮缠的糖多糖少要求后,老师需要准备的糖个数的最小值
思考:
- 怎么去结合这两种题目要求呢,一开始能想到的处理就是先处理关系闭包问题,同时用一个ans去记录老师需要准备的糖的数量从最少的1个开始,关系环到一个一个往后的大于关系也只是加1(这个时候又想到记录方案数那题),如果是相等的关系,当前节点的方案数是前一点的一倍,如果大于关系,当前节点的方案数等于前个节点加一,最后老师需要准备糖果的个数就是总的方案数,该觉还是可以spfa走一波,开一个cnt数组那样子
- 这样要怎么考虑所有关系呢
听y说:
- 二刷视频
- 我悟了。就是这题它并没有直接给出我需要给某个小朋友多少糖,只给出了每个小朋友糖的相对数量关系,而我们需要一个超级源点,去遍历到所有的边。
- 关于求最小值用最长路建边并做spfa求最长路,
#include<bits/stdc++.h>using namespace std;
#define int long long
int n,k;
const int N=1e5+10,M=3*N;
int h[N],e[M],ne[M],w[M],idx;
int d[N],q[N],st[N],cnt[N];void add(int a,int b,int c){e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}bool spfa(){memset(d,-0x3f,sizeof d);d[0]=0;int hh=0,tt=1;q[hh]=0;while(hh!=tt){int t=q[--tt];if(tt==N) tt=0;st[t]=0;for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];if(d[j]<d[t]+w[i]){d[j]=d[t]+w[i];cnt[j]=cnt[t]+1;if(cnt[j]>=n+1) return 0;if(!st[j]){st[j]=1;q[tt++]=j;if(tt==N)tt=0;}}}}return 1;
}signed main(){scanf("%lld%lld",&n,&k);memset(h,-1,sizeof h);for(int i=0;i<k;i++){int x,a,b;scanf("%lld%lld%lld",&x,&a,&b);if(x==1) add(b,a,0),add(a,b,0);else if(x==2) add(a,b,1);else if(x==3) add(b,a,0);else if(x==4) add(b,a,1);else if(x==5) add(a,b,0);}for(int i=1;i<=n;i++) add(0,i,1);int res=spfa();if(!res){puts("-1");}else{res=0;for(int i=1;i<=n;i++) res+=d[i];printf("%lld",res);}return 0;
}
用先进先出的栈
2. 区间
第一眼:
- 如果要让Z包含的数尽可能少,那就让一个区间的数集中在和其他区间相交的部分就可以贪心的解决这个问题了吧
听y说:
- 用差分约束的思想做很牛逼,同时利用前缀和的思想,绝绝子
- 两个端点约束关系:
- s [ i ] ≥ s [ i − 1 ] s[i] \ge s[i-1] s[i]≥s[i−1]
- $s[i]-s[i-1] \le 1 $
- [ a , b ] [a,b] [a,b] s [ b ] − s [ a − 1 ] ≥ c s[b]-s[a-1] \ge c s[b]−s[a−1]≥c
- 转换成最短路模型——求最长路
- s [ i ] ≥ s [ i − 1 ] s[i] \ge s[i-1] s[i]≥s[i−1]
- s [ i − 1 ] ≥ s [ i ] − 1 s[i-1] \ge s[i] -1 s[i−1]≥s[i]−1
- s [ b ] ≥ s [ a − 1 ] + c s[b] \ge s[a-1]+c s[b]≥s[a−1]+c
- 接着就是模版框框的打了
#include<bits/stdc++.h>using namespace std;
//关于这里的N和M的取值,因为M代表边数,我们以最大值N建边
//最多会建3*N条边,如果也算上第N个点的位置的话,会数组越界,可以开大一个数量级
//因为并没有特别熟练空间复杂度以及并不料想到后他数据怎么卡,就开大一个数量级过了
const int N=5e4+10,M=4*N+10;
int m;
int h[N],e[M],w[M],ne[M],idx;
int d[N],st[N],cnt[N],q[N];void add(int a,int b,int c){e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}bool spfa(){memset(d,-0x3f,sizeof d);d[0]=0;int hh=0,tt=1;while(hh!=tt){int t=q[hh++];if(hh==N) hh=0;st[t]=0;for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];if(d[j]<d[t]+w[i]){d[j]=d[t]+w[i];if(!st[j]){st[j]=1;q[tt++]=j;if(tt==N) tt=0;}}}}}signed main(){scanf("%d",&m);memset(h,-1,sizeof h);for(int i=1;i<N;i++){add(i-1,i,0);add(i,i-1,-1);}while(m--){int a,b,c;scanf("%d%d%d",&a,&b,&c);a++,b++;add(a-1,b,c);}spfa();printf("%d",d[50001]);return 0;
}
3. 排队布局
第一眼:
- 又是usaco的牛,好多事的牛
- 1和n之间可以任意大,说明1或n都没有能够约束他们的其他牛
- 不存在满足要求,是不是两头牛之间的约束关系会存在矛盾
思考:
学差分约束的时候时把这道题和糖果(第一题)进行着对比来学的
- 糖果——求最小值——求最长路
- 本题——求最大值
索性先自己思考并落实一遍
- 两头牛之间距离差分约束关系
- 原本编号的先后顺序: s [ i ] < = s [ i + 1 ] s[i]<=s[i+1] s[i]<=s[i+1]
- 前 M L M_L ML条边: s [ a ] − s [ b ] ≤ c s[a]-s[b] \le c s[a]−s[b]≤c
- 后 M D M_D MD条边: s [ a ] − s [ b ] ≥ c s[a]-s[b] \ge c s[a]−s[b]≥c
- 最短路;
- s [ i ] < = s [ i + 1 ] s[i]<=s[i+1] s[i]<=s[i+1]
- s [ a ] ≤ s [ b ] + c s[a] \le s[b]+c s[a]≤s[b]+c
- s [ b ] ≤ s [ a ] − c s[b] \le s[a]-c s[b]≤s[a]−c
- Tips:
为什么n一定能到其他所有边?
- 因为第一条约束关系
隐含的前缀和思想
#include<bits/stdc++.h>using namespace std;
//需要思考怎么建边,约束条件如何化成边
const int N=1e3+10,M=2e4+10+2*N,INF=0x3f3f3f3f;
int n,l,d;
int h[N],e[M],w[M],ne[M],idx;
int dist[N],st[N],cnt[N],q[N];void add(int a,int b,int c){e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}int spfa(int s){memset(dist,0x3f,sizeof dist);memset(st,0,sizeof st);memset(cnt,0,sizeof cnt);dist[s]=0;int hh=0,tt=1;q[hh]=s;while(hh!=tt){int t=q[hh++];if(hh==N) hh=0;st[t]=0;for(int i=h[t];i!=-1;i=ne[i]){int j=e[i]; if(dist[j]>dist[t]+w[i]){dist[j]=dist[t]+w[i];cnt[j]=cnt[t]+1;if(cnt[j]>=n+1) return -1;if(!st[j]){st[j]=1;q[tt++]=j;if(tt==N) tt=0;}}}}return 1;
}signed main(){cin>>n>>l>>d;memset(h,-1,sizeof h);for(int i=1;i<n;i++){add(i+1,i,0);add(0,i,0);}add(0,n,0);for(int i=0;i<l;i++){int a,b,c;scanf("%d%d%d",&a,&b,&c);if(a>b) swap(a,b);add(a,b,c);}for(int i=0;i<d;i++){int a,b,c;scanf("%d%d%d",&a,&b,&c);if(a>b) swap(a,b);add(b,a,-c);}int res=spfa(0);if(res==-1) puts("-1");else{spfa(1);printf("%d",dist[n]==INF?-2:dist[n]);}return 0;
}
4. 雇佣收银员
第一眼:
- 感觉和区间会很像,一个时间段如果需要很多营业员,那么就让每个营业员的工作时间尽可能覆盖到这个区间
- 输入处理比较麻烦(第二眼,实际上还好,就是变量表示需要思考一下
思考:
建边操作:
用区间表示所需,采用前缀和来建立关系:
某一时刻的营收员人数要大于等于这一时刻所需要的营收员人数
上岗人数不能超过申请人数
以时刻作为点
i时刻所需,i时刻申请,像是隐含的关系,就是一个人工作时长导致的区间内申请人数的变化仍然需要满足能够在某时刻大于等于所需的人数
然后是某一时刻的申请人数也可以用前缀和来表示,于是可以x[i]的建边也可以用 s u m [ i ] − s u m [ i − 1 ] sum[i]-sum[i-1] sum[i]−sum[i−1]来表示
s u m [ i ] sum[i] sum[i] x [ i ] x[i] x[i] r [ i ] r[i] r[i] n u m [ i ] num[i] num[i]
上岗人数约束关系如下:
- 某一时刻的上岗人数 : s u m [ i ] − s u m [ i − 1 ] > = 0 sum[i]-sum[i-1]>=0 sum[i]−sum[i−1]>=0
- 某一时刻的上岗和申请人数之间的关系: s u m [ i ] − s u m [ i − 1 ] ≤ n u m [ i ] sum[i]-sum[i-1]\le num[i] sum[i]−sum[i−1]≤num[i]
- 某一时刻的所在的上岗人数,
- [ 1 , 7 ] [1,7] [1,7]: s u m [ i ] + s u m [ 24 ] − s u m [ 24 − ( 7 − i ) ] > = r [ i ] sum[i]+sum[24]-sum[24-(7-i)]>=r[i] sum[i]+sum[24]−sum[24−(7−i)]>=r[i]即 s u m [ i ] + s u m [ 24 ] − s u m [ 16 + i ] > = r [ i ] sum[i]+sum[24]-sum[16+i]>=r[i] sum[i]+sum[24]−sum[16+i]>=r[i]
- [ 8 , 24 ] [8,24] [8,24]: s u m [ i ] − s u m [ i − 8 ] > = r [ i ] sum[i]-sum[i-8]>=r[i] sum[i]−sum[i−8]>=r[i]
转换成最短路模型如下:
- s u m [ i ] > = s [ i − 1 ] sum[i]>=s[i-1] sum[i]>=s[i−1]
- s u m [ i − 1 ] > = s u m [ i ] − n u m [ i ] sum[i-1]>=sum[i]-num[i] sum[i−1]>=sum[i]−num[i]
- s u m [ i ] > = s u m [ 16 + i ] − s u m [ 24 ] + r [ i ] sum[i]>=sum[16+i]-sum[24]+r[i] sum[i]>=sum[16+i]−sum[24]+r[i]
- s u m [ i ] > = r [ i ] + s u m [ i − 8 ] sum[i]>=r[i]+sum[i-8] sum[i]>=r[i]+sum[i−8]
#include<bits/stdc++.h>using namespace std;
const int N=30,M=100;
int n,sum[N],r[N],num[N];
int h[N],e[M],ne[M],w[M],idx;
int cnt[N],st[N],q[M],d[N];void add(int a,int b,int c){e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}void build(int c){memset(h,-1,sizeof h);idx=0;for(int i=1;i<=24;i++){add(i-1,i,0);add(i,i-1,-num[i]);}for(int i=1;i<=7;i++) add(16+i,i,r[i]-c);for(int i=8;i<=24;i++) add(i-8,i,r[i]);add(0,24,c),add(24,0,-c);
}bool spfa(int s24){memset(d,-0x3f,sizeof d);memset(st,0,sizeof st);memset(cnt,0,sizeof cnt);build(s24);int hh=0,tt=1;d[0]=0;q[0]=0;while(hh!=tt){int t=q[hh++];if(hh==N) hh=0;st[t]=0;for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];if(d[j]<d[t]+w[i]){d[j]=d[t]+w[i];cnt[j]=cnt[t]+1;if(cnt[j]>=25) return false;if(!st[j]){st[j]=1;q[tt++]=j;if(tt==N) tt=0;}}}}return true;
}signed main(){int t;cin>>t;while(t--){memset(num,0,sizeof num);for(int i=1;i<=24;i++){scanf("%d",r+i);}cin>>n;while(n--){int x;scanf("%d",&x);num[x+1]++;}int res=0;for(int i=0;i<=1000;i++){//res=spfa(i);if(spfa(i)){res=1;cout<<i<<endl;break;}}if(!res)puts("No Solution");}return 0;
}
2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
4
0
8
16
16
相关文章:

最短路算法——差分约束
差分约束 (1) 求不等式组的可行解 源点:从源点出发,一定可以走到所有的边求可行解步骤: 先将每个不等式 x i ≤ x j c x_i \le x_j c xi≤xjc,转化成一条从 s j s_j sj走到 s i s_i si,长度为 c k c_k ck 的一条边找…...

Log4j日志框架讲解(全面,详细)
目录 Log4j概述 log4j的架构(组成) Loggers Appenders Layouts 快速入门 依赖 java代码 日志的级别 log4j.properties 自定义Logger 总结: Log4j概述 Log4j是Apache下的一款开源的日志框架,通过在项目中使用 Log4J&…...
LeetCode 35, 242, 994
目录 35. 搜索插入位置题目链接标签思路代码 242. 有效的字母异位词题目链接标签思路代码 994. 腐烂的橘子题目链接标签思路代码 35. 搜索插入位置 题目链接 35. 搜索插入位置 标签 数组 二分查找 思路 本题与 704. 二分查找 十分相似,只不过本题在找不到 tar…...

ctfshow-web入门-文件包含(web87)巧用 php://filter 流绕过死亡函数的三种方法
目录 方法1:php://filter 流的 base64-decode 方法 方法2:通过 rot13 编码实现绕过 方法3:通过 strip_tags 函数去除 XML 标签 除了替换,新增 file_put_contents 函数,将会往 $file 里写入 <?php die(大佬别秀了…...
adb shell ps -T打印出来参数的含义,以及D,T,Z代表的状态含义是什么?
在Android系统中,使用adb shell ps命令可以查看当前系统中运行的进程信息。当你添加-T选项时(注意,标准的ps命令在Android的adb shell中可能不直接支持-T选项,这通常与Linux中的ps命令略有不同),你可能是想…...
leetcode77组合——经典回溯算法
本文主要讲解组合的要点与细节,以及回溯算法的解题步骤,按照步骤思考更方便理解 c和java代码如下,末尾 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 具体要点: …...

springcloud-alibba之FeignClient
代码地址:springcloud系列: springcloud 组件分析拆解 1.FeignClient的集成 springboot版本:3.1.5 springcloud组件版本:2022.0.4 nacos客户端的版本:2.3.2 1.引pom 这里引入了nacos和feginclient的版本 <dependency>…...

三、docker配置阿里云镜像仓库并配置docker代理
一、配置阿里云镜像仓库 1. 登录阿里云官网,并登录 https://www.aliyun.com/ 2. 点击产品 - 容器 - 容器与镜像服务ACR - 管理控制台 - 镜像工具 - 镜像加速器 二、配置docker代理 #1. 创建docker相关的systemd文件 mkdir -p /etc/systemd/system/docker.servic…...

【面向就业的Linux基础】从入门到熟练,探索Linux的秘密(十一)-git(3)
Git是目前最流行的版本控制系统之一,在现代软件开发中扮演着重要的角色。它能够有效地跟踪文件变化、协作开发,并存储项目的历史记录。本文的目的是向读者介绍Git的基本概念和工作原理,帮助初学者快速上手使用Git,并帮助有经验的开…...

全面解析 TypeScript 泛型的二三事
2024年了相信大家都已经在日常开发的过程中使用上了 TypeScript 了。TypeScript 增强了代码可靠性和可维护性,确保减少运行时错误并提高开发人员的工作效率。 TypeScript 通过类型声明 使得 javascript 拥有了强类型校验。而泛型的是类型声明中最重要的一环&#x…...

单/多线程--协程--异步爬虫
免责声明:本文仅做技术交流与学习... 目录 了解进程和线程 单个线程(主线程)在执行 多线程 线程池 协程(爬虫多用) 假异步:(同步) 真异步: 爬虫代码模版 异步-爬虫 同步效果--19秒 异步效果--7秒 了解进程和线程 # --------------------> # ------> # …...
android pdf框架-11,查看图片
前10篇文章,9章关于pdf的,pdf解析后,里面也是有各种图片,于是利用pdf的view来展示图片,似乎也是个不错的想法. android手机中的图片查看功能,有的可以展示,有的不能.比如华为,荣耀对大体积的png是可以显示的,小米是不显示,只有缩略图. 一张png50m大,比如清明上河图,原图是tif…...
【CSS】深入浅出弹性布局
CSS的弹性布局(Flexbox)是一种用于在容器中沿着一维方向(水平或垂直)来布局、对齐和分配容器内项目空间的有效方式。它旨在提供一个更加有效的方式来布局、对齐和分配容器中项目的空间,即使它们的大小未知或是动态变化…...

医院挂号系统小程序的设计
管理员账户功能包括:系统首页,个人中心,患者管理,医生管理,专家信息管理,科室管理,预约信息管理,系统管理 微信端账号功能包括:系统首页,专家信息࿰…...

广州外贸建站模板
Yamal外贸独立站wordpress主题 绿色的亚马尔Yamal外贸独立站wordpress模板,适用于外贸公司建独立站的wordpress主题。 https://www.jianzhanpress.com/?p7066 赛斯科Sesko-W外贸建站WP主题 适合机械设备生产厂家出海做外贸官网的wordpress主题,红橙色…...

KDP数据分析实战:从0到1完成数据实时采集处理到可视化
智领云自主研发的开源轻量级Kubernetes数据平台,即Kubernetes Data Platform (简称KDP),能够为用户提供在Kubernetes上的一站式云原生数据集成与开发平台。在最新的v1.1.0版本中,用户可借助 KDP 平台上开箱即用的 Airflow、AirByte、Flink、K…...

【人工智能】-- 智能机器人
个人主页:欢迎来到 Papicatch的博客 课设专栏 :学生成绩管理系统 专业知识专栏: 专业知识 文章目录 🍉引言 🍉机器人介绍 🍈机器人硬件 🍍机械结构 🍍传感器 🍍控…...

Android广播机制
简介 某个网络的IP范围是192.168.0.XXX,子网 掩码是255.255.255.0,那么这个网络的广播地址就是192.168.0.255。广播数据包会被发送到同一 网络上的所有端口,这样在该网络中的每台主机都将会收到这条广播。为了便于进行系统级别的消息通知&…...
SQL FOREIGN KEY
SQL FOREIGN KEY 简介 SQL(Structured Query Language)是用于管理关系数据库管理系统(RDBMS)的标准编程语言。在SQL中,FOREIGN KEY是一个重要的概念,用于建立和维护数据库中不同表之间的关系。本文将详细介绍SQL FOREIGN KEY的概念、用途、以及如何在SQL中实现和使用FO…...

绘唐3最新版本哪里下载
绘唐3最新版本哪里下载 绘唐最新版本下载地址 推文视频创作设计是一种通过视频和文字的形式来进行推广的方式,可以通过一些专业的工具来进行制作。 以下是一些常用的小说推文视频创作设计工具: 视频剪辑软件:如Adobe Premiere Pro、Fina…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...

蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...