From 08c804bb361ae3bc26ff55cc9c79c69940f4ae35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Mon, 25 Dec 2023 07:48:44 +0800 Subject: [PATCH 01/87] 'commit' --- TangDou/AcWing/Math/QiWang/217.drawio | 1 - TangDou/AcWing/Math/QiWang/217.md | 206 ------------------ TangDou/AcWing/Math/QiWang/217_DaoTui.cpp | 44 ---- TangDou/AcWing/Math/QiWang/217_ZhengTui.cpp | 59 ----- TangDou/AcWing/Math/QiWang/218.md | 152 ------------- TangDou/AcWing/Math/QiWang/218_dp.cpp | 48 ---- .../T5/QiWang/218.cpp} | 0 TangDou/AcWing_TiGao/T5/QiWang/218_dfs.cpp | 70 ------ TangDou/AcWing_TiGao/T5/QiWang/218_dp.cpp | 48 ---- 9 files changed, 628 deletions(-) delete mode 100644 TangDou/AcWing/Math/QiWang/217.drawio delete mode 100644 TangDou/AcWing/Math/QiWang/217.md delete mode 100644 TangDou/AcWing/Math/QiWang/217_DaoTui.cpp delete mode 100644 TangDou/AcWing/Math/QiWang/217_ZhengTui.cpp delete mode 100644 TangDou/AcWing/Math/QiWang/218.md delete mode 100644 TangDou/AcWing/Math/QiWang/218_dp.cpp rename TangDou/{AcWing/Math/QiWang/218_dfs.cpp => AcWing_TiGao/T5/QiWang/218.cpp} (100%) delete mode 100644 TangDou/AcWing_TiGao/T5/QiWang/218_dfs.cpp delete mode 100644 TangDou/AcWing_TiGao/T5/QiWang/218_dp.cpp diff --git a/TangDou/AcWing/Math/QiWang/217.drawio b/TangDou/AcWing/Math/QiWang/217.drawio deleted file mode 100644 index d9a22c8..0000000 --- a/TangDou/AcWing/Math/QiWang/217.drawio +++ /dev/null @@ -1 +0,0 @@ -5VpLc9owEP41zLSHMtbLj2NDHj00M+mkbdLePFiAEmNRI4rJr6+MZbAlB0gCVgkzHLwraWV9+3m1WtFBvXF2lYaT0TWPaNyBTpR10HkHQoAh7OQ/J1oUGt8jhWKYskh1Witu2RNVSkdpZyyi01pHwXks2KSu7PMkoX1R04Vpyuf1bgMe12edhENqKG77YWxq71gkRkrrErxu+ELZcFRODdygaBmHZW+1lOkojPi8okIXHdRLORfF0zjr0ThHrwSmGHf5TOvqzVKaiF0GDB7cy3F2+fPp4QpP/izI7Ovi/pOy8jeMZ2rFD+ptxaLEQJqRcEvhbD5igt5Own7eMpcel7qRGMdSAvIxnE4KHwxYRuWsZ1OR8scVcFhqBjwRyssQlbKaqmFF5evRVNCsolIrvKJ8TEW6kF1Ua4AV2opvIFDyfO09olSjit9KXaj4MlxZXiMqHxSozQBf9JxfT78ZFL9/XH0PxfUw/PatAeAM2EbYgLMB9GcRdoM6wtCxjDA0EUZHjbDneTWEEbKMMDIRhkeNcKBxGNvmMDYRfjxqhH2/jjCxzWFiIJwcNcAu1AC2TWHXANjEN4k+5zmZlPpxOJ2yfh3WlM+SKMfz3NEQ9KRMMybuVVv+/Ct/7hIlnWeVpvNFKSRyaffK/FKojMrF9bClVI4z/VkshkZGumgkK1M+S/t0e84lwnRIxbbUwSTAjg5OaRwK9rf+uk1eVzPccCYXst6EHC1EehpximWqUdW0UzdENENQM1TgYBhaknC17Nfz0jN4edeQgckPUdTJWFCgx2OeSk3CkzwuDFgca6owZsMkZ7TkAZX6s/yzZvIE8Vk1jFkUxc8Flc2U30cg1uIE8M044TbQCB0qTvitxYku9CuhArwmULw4TORGb2jKJFY5GZa9LIQOaDN0BNoXLw3vJ3RApBk6cOgI2qMq2Zmp/8XWhK3yy8V1Wug5za788n3YdT3PdQMijWpWEfa6aHn4klsfQq7jtsq9sgB2ePKBN1BvFTYrKdUugdMCZZFVypZlz7dSNgBBTlnfgW7g5QyumSXY6WIMCCI+CggO2mWsWeo6VLh8wQlgA2e1MwHcwtmltJ+9fVOVZSuRiVUiBxtCpsztq40owK8jOXBg/XPBoN0zAzBrinum8u603HZU3SMD8TEwEDj6yVTPCnelmavXqFo+mQKzrnqogPmC/PK1W/q+8tJNtyj/NzNPIy81S9Ut7PJvyEs9WI+nEi+7+zw8Bi6/oyhrVv7vGi4IT6YACD3LBUBg3hTcNdwnvluHGJ8Wari6adcjTTVy8/7x3XrE9zSHNPxppF2HmEXywYcMfDwdl7hAcwm27RKzGCxdgk7IJZ7216rVRZ4tl5Tz110CT8gl+t6Obe/t0KwBSpc8npBL9DsrQg7mEimu/z1apMvrP+Gii38= \ No newline at end of file diff --git a/TangDou/AcWing/Math/QiWang/217.md b/TangDou/AcWing/Math/QiWang/217.md deleted file mode 100644 index 83cfec7..0000000 --- a/TangDou/AcWing/Math/QiWang/217.md +++ /dev/null @@ -1,206 +0,0 @@ -##[$AcWing$ $217$. 绿豆蛙的归宿](https://www.acwing.com/problem/content/description/219/) - -### 一、题目描述 -给出一个有向无环的连通图,起点为 $1$ ,终点为 $N$,每条边都有一个长度。 - -数据保证从起点出发能够到达图中所有的点,图中所有的点也都能够到达终点。 - -绿豆蛙从起点出发,走向终点。 - -到达每一个顶点时,如果有 $K$ 条离开该点的道路,绿豆蛙可以选择任意一条道路离开该点,并且走向每条路的概率为 $1/K$。 - -现在绿豆蛙想知道,从起点走到终点所经过的路径总长度的 **期望** 是多少? - -**输入格式** -第一行: 两个整数 $N,M$,代表图中有 $N$ 个点、$M$ 条边。 - -第二行到第 $1+M$ 行: 每行 $3$ 个整数 $a,b,c$,代表从 $a$ 到 $b$ 有一条长度为 $c$ 的有向边。 - -**输出格式** -输出从起点到终点路径总长度的 **期望值**,结果四舍五入保留两位小数。 - -**数据范围** -$1≤N≤10^5,1≤M≤2N$ - -**输入样例:** -```cpp {.line-numbers} -4 4 -1 2 1 -1 3 2 -2 3 3 -3 4 4 -``` - -**输出样例:** -```cpp {.line-numbers} -7.00 -``` - -### 二、数学期望 - -**[视频讲解 数学期望及性质](https://www.ixigua.com/6978816201023554061)** - -**[参考题解](https://www.acwing.com/solution/content/63508/)** - -首先明白一点:到达某个结果的期望值 = 这个结果 * 从起始状态到这个状态的概率 - -$Q:$什么意思呢? - -如图: -
- -我们计算从$1$号点到$3$号点的期望距离 - -路径$1$. $\displaystyle 1−>3:E_1=2×\frac{1}{2}=1$ - -路径$2$. $\displaystyle 1−>2−>3:E_2=1×\frac{1}{2}+3×\frac{1}{2}×1=2$ - -这里路径$2$中从$1$到$2$概率为$\displaystyle \frac{1}{2}$,但单看从$2$到$3$概率就是$1$,但是从$1$到$3$那就是从($1$到$2$的概率)$\displaystyle \frac{1}{2}$×$1$($2$到$3$的概率)=$\displaystyle \frac{1}{2}$。  - -所以从 点$1$ 到 点$3$ 的数学期望值=$1+2=3$ - -

总结:概率是叠乘的

- - -本题有 **正推** 和 **倒推** 两种写法: - -### 二、正推法 -
- -设: -- $a_1, a_2, a_3 … a_k$ 到 $j$ 的权值为 $w_1, w_2, w_3 … w_k$, -- 从起点到这$k$个点的概率为:$p_1, p_2, p_3 … p_k$ -- 每个点的出度为:$out_1, out_2, out_3, … , out_k$ - -这里的$1\sim k$个点的从起点的到该点的概率一定是确定的,也就是说这个点的概率是被更新完的,即此时这个点的入度为$0$! - -那么就有: -$$f(i):表示从起点到i点的期望距离$$ -$$f(j)=\frac{f(1)+w_1\times p_1}{out_1}+\frac{f(2)+w_2\times p_2}{out_2}+\frac{f(3)+w_3\times p_3}{out_3}+...+\frac{f(k)+w_k\times p_k}{out_k} $$ - -#### 正推代码 -```cpp {.line-numbers} -#include -using namespace std; -const int N = 1e5 + 10, M = 2 * N; - -//邻接表 -int h[N], e[M], ne[M], w[M], idx; -void add(int a, int b, int c) { - e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; -} - -int n, m; // n个顶点,m条边 - -int out[N], in[N]; //出度,入度 -double f[N], g[N]; // f:数学期望结果 g:概率 - -void topsort() { - queue q; - //起点为1,起点的概率为100% - q.push(1); - g[1] = 1.0; - f[1] = 0.0; - - // DAG,执行拓扑序,以保证计算的顺序正确,确保递归过程中,前序数据都已处理完毕 - while (q.size()) { - auto u = q.front(); - q.pop(); - - for (int i = h[u]; ~i; i = ne[i]) { //枚举的是每边相邻边 - int j = e[i]; //此边,一端是t,另一端是j - //此边边条w[i] - f[j] += (f[u] + w[i] * g[u]) / out[u]; - g[j] += g[u] / out[u]; // p[j]也需要概率累加 - //拓扑序的标准套路 - in[j]--; - if (!in[j]) q.push(j); - } - } -} - -int main() { - //初始化邻接表 - memset(h, -1, sizeof h); - cin >> n >> m; - - while (m--) { - int a, b, c; - cin >> a >> b >> c; - add(a, b, c); - //维护出度,入度 - out[a]++, in[b]++; - } - //拓扑序 - topsort(); - - //正向递推,输出结果,保留两位小数 - printf("%.2lf", f[n]); - - return 0; -} -``` -### 三、倒推法 -现在学会了正推,来看看 **逆推**,即 **从终点找到起点** - - -
- -设 $f[x]$ 表示结点 $x$ 走到终点所经过的路径的期望长度。显然 $f[n]=0$ ,最后要求 $f[1]$ 。 - -一般来说,**初始状态确定时可用顺推,终止状态确定时可用逆推**。 - -设 $x$ 出发有 $k$ 条边,分别到达 $y_1,y_2...y_k$ ,边长分别为 $z_1,z_2...z_k$ ,根据数学期望的定义和性质,有: - -$$f[x]=\frac 1 k\times (f[y_1]+z_1)+\frac 1 k\times (f[y_2]+z_2)+...+\frac 1 k\times (f[y_k]+z_k)=\frac 1 k \times \sum_{i=1}^k(f[y_i]+z_i)$$ -根据设定已经确定是能够到达 $n$ 点了,概率为 $1$ 。 - -$f[n]$ 已知,需要求解 $f[1]$ ,建立 **反向图**,按照 **拓扑序** 求解。 - -#### 倒推代码 -```cpp {.line-numbers} -#include -using namespace std; -const int N = 100010, M = N << 1; -int n, m; -int in[N], g[N]; //入度,入度的备份数组,原因:in在topsort中会不断变小受破坏 - -double f[N]; - -//链式前向星 -int e[M], h[N], idx, w[M], ne[M]; -void add(int a, int b, int c = 0) { - e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; -} - -void topsort() { - queue q; - q.push(n); - f[n] = 0; // n到n的距离期望是0 - while (q.size()) { - int u = q.front(); - q.pop(); - for (int i = h[u]; ~i; i = ne[i]) { //枚举每条入边(因为是反向图) - int j = e[i]; - f[j] += (f[u] + w[i]) / g[j]; - in[j]--; - if (in[j] == 0) q.push(j); - } - } -} -int main() { - memset(h, -1, sizeof(h)); - cin >> n >> m; - - while (m--) { - int a, b, c; - cin >> a >> b >> c; - add(b, a, c); //反向图,计算从n到1 - in[a]++; //入度 - g[a] = in[a]; //入度数量 - } - topsort(); - printf("%.2lf\n", f[1]); - return 0; -} -``` \ No newline at end of file diff --git a/TangDou/AcWing/Math/QiWang/217_DaoTui.cpp b/TangDou/AcWing/Math/QiWang/217_DaoTui.cpp deleted file mode 100644 index ba0fe13..0000000 --- a/TangDou/AcWing/Math/QiWang/217_DaoTui.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -using namespace std; -const int N = 100010, M = N << 1; -int n, m; -int in[N], g[N]; //入度,入度的备份数组,原因:in在topsort中会不断变小受破坏 - -double f[N]; - -//链式前向星 -int e[M], h[N], idx, w[M], ne[M]; -void add(int a, int b, int c = 0) { - e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; -} - -void topsort() { - queue q; - q.push(n); - f[n] = 0; // n到n的距离期望是0 - while (q.size()) { - int u = q.front(); - q.pop(); - for (int i = h[u]; ~i; i = ne[i]) { //枚举每条入边(因为是反向图) - int j = e[i]; - f[j] += (f[u] + w[i]) / g[j]; - in[j]--; - if (in[j] == 0) q.push(j); - } - } -} -int main() { - memset(h, -1, sizeof(h)); - cin >> n >> m; - - while (m--) { - int a, b, c; - cin >> a >> b >> c; - add(b, a, c); //反向图,计算从n到1 - in[a]++; //入度 - g[a] = in[a]; //入度数量 - } - topsort(); - printf("%.2lf\n", f[1]); - return 0; -} \ No newline at end of file diff --git a/TangDou/AcWing/Math/QiWang/217_ZhengTui.cpp b/TangDou/AcWing/Math/QiWang/217_ZhengTui.cpp deleted file mode 100644 index 05e64ee..0000000 --- a/TangDou/AcWing/Math/QiWang/217_ZhengTui.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include -using namespace std; -const int N = 1e5 + 10, M = 2 * N; - -//邻接表 -int h[N], e[M], ne[M], w[M], idx; -void add(int a, int b, int c) { - e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; -} - -int n, m; // n个顶点,m条边 - -int out[N], in[N]; //出度,入度 -double f[N], g[N]; // f:数学期望结果 g:概率 - -void topsort() { - queue q; - //起点为1,起点的概率为100% - q.push(1); - g[1] = 1.0; - f[1] = 0.0; - - // DAG,执行拓扑序,以保证计算的顺序正确,确保递归过程中,前序数据都已处理完毕 - while (q.size()) { - auto u = q.front(); - q.pop(); - - for (int i = h[u]; ~i; i = ne[i]) { //枚举的是每边相邻边 - int j = e[i]; //此边,一端是t,另一端是j - //此边边条w[i] - f[j] += (f[u] + w[i] * g[u]) / out[u]; - g[j] += g[u] / out[u]; // p[j]也需要概率累加 - //拓扑序的标准套路 - in[j]--; - if (!in[j]) q.push(j); - } - } -} - -int main() { - //初始化邻接表 - memset(h, -1, sizeof h); - cin >> n >> m; - - while (m--) { - int a, b, c; - cin >> a >> b >> c; - add(a, b, c); - //维护出度,入度 - out[a]++, in[b]++; - } - //拓扑序 - topsort(); - - //正向递推,输出结果,保留两位小数 - printf("%.2lf", f[n]); - - return 0; -} \ No newline at end of file diff --git a/TangDou/AcWing/Math/QiWang/218.md b/TangDou/AcWing/Math/QiWang/218.md deleted file mode 100644 index a4c716a..0000000 --- a/TangDou/AcWing/Math/QiWang/218.md +++ /dev/null @@ -1,152 +0,0 @@ -##[$AcWing$ $218$. 扑克牌 ](https://www.acwing.com/problem/content/description/220/) - -### 一、题目描述 -$Admin$ 生日那天,$Rainbow$ 来找 $Admin$ 玩扑克牌。 - -玩着玩着 $Rainbow$ 觉得太没意思了,于是决定给 $Admin$ 一个考验。 - -$Rainbow$ 把一副扑克牌($54$张)随机洗开,倒扣着放成一摞。 - -然后 $Admin$ 从上往下依次翻开每张牌,每翻开一张黑桃、红桃、梅花或者方块,就把它放到对应花色的堆里去。 - -$Rainbow$ 想问问 $Admin$,得到 $A$ 张黑桃、$B$ 张红桃、$C$ 张梅花、$D$ 张方块需要翻开的牌的张数的期望值 $E$ 是多少? - -特殊地,如果翻开的牌是大王或者小王,$Admin$ 将会把它作为某种花色的牌放入对应堆中,使得放入之后 $E$的值尽可能小。 - -由于 $Admin$ 和 $Rainbow$ 还在玩扑克,所以这个程序就交给你来写了。 - -**输入格式** -输入仅由一行,包含四个用空格隔开的整数,$A,B,C,D$。 - -**输出格式** -输出需要翻开的牌数的期望值 $E$,四舍五入保留 $3$ 位小数。 - -如果不可能达到输入的状态,输出 `-1.000`。 - -**数据范围** -$0≤A,B,C,D≤15$ - -**输入样例:** -```cpp {.line-numbers} -1 2 3 4 -``` - -**输出样例:** -```cpp {.line-numbers} -16.393 -``` - -### 二、题意分析 - -$Q$:为什么从终止状态向起始状态递推? - -**答**:满足条件的终止状态较多,而起始状态唯一。考虑以终止状态为初值,起始状态为目标,进行动态规划。 - -#### 状态表示 -$f[a][b][c][d][x][y]$ : 当前已翻开状态下,还需翻开牌的数量 **期望数**。 - -- $a,b,c,d$ 为已翻开的各类牌 (黑红花片) 的数量 -- $x,y$代表大、小王的状态($0$为未翻开,$1$代表已翻开且当做黑桃,以此类推), 设 $rst$ 为剩余牌的数量。 -$$rst=54-a-b-c-d-(x!=0)-(y!=0)$$ - -若 $a < 13$,则当前抽到黑桃的贡献为 - -$$\frac{13−a}{rst} \times f[a+1][b][c][d][x][y]$$ - -其余花色同理。若小王被抽取,取可转移状态期望最小的一个进行状态转移,其贡献为: - -$$\frac{1}{rst} \times \min_{1≤i≤4}f[a][b][c][d][i][y]$$ - -大王同理。 - -​记忆化搜索求解,若无牌可抽仍未到达 $a > = A \&\& b > = B \&\& c > = C \&\& d > = D$ 的终止状态,则期望为正无穷,代表不合法的状态。 - - -#### 三、实现代码 - -```cpp {.line-numbers} -#include -using namespace std; -const int N = 15; -const int INF = 0x3f3f3f3f; -double f[N][N][N][N][5][5]; -int st[N][N][N][N][5][5]; -int A, B, C, D; - -//如果大小王翻出来放1里,则a++,放2里b++,... -void add(int &a, int &b, int &c, int &d, int x) { - if (x == 1) a++; - if (x == 2) b++; - if (x == 3) c++; - if (x == 4) d++; -} - -/* -功能:计算当前状态f(a,b,c,d,x,y)下的期望值 -*/ -double dfs(int a, int b, int c, int d, int x, int y) { - //记忆化,同时因为f为double类型,不能使用传统的memset(0x3f)之类 - //进行初始化并判断是否修改过,只能再开一个st数组 - if (st[a][b][c][d][x][y]) return f[a][b][c][d][x][y]; - st[a][b][c][d][x][y] = 1; - - //递归出口:当前状态是否到达目标状态,目标状态的期望值是0 - int ta = a, tb = b, tc = c, td = d; //抄出来 - add(ta, tb, tc, td, x), add(ta, tb, tc, td, y); //大王小王会改变四个花色的数量 - if (ta >= A && tb >= B && tc >= C && td >= D) return 0; - - //当前状态下的剩余牌数量 - int rst = 54 - ta - tb - tc - td; - if (rst == 0) return INF; //还没有完成目标,没有剩余的牌了,无解 - - //当前状态可以向哪些状态转移 - // Q:v为什么要初始化为1? - // A:看题解内容 - double v = 1; - if (a < 13) //黑桃有剩余,可能选出的是黑桃 - v += dfs(a + 1, b, c, d, x, y) * (13 - a) / rst; - if (b < 13) //红桃有剩余,可能选出的是红桃 - v += dfs(a, b + 1, c, d, x, y) * (13 - b) / rst; - if (c < 13) //梅花有剩余,可能选出的是梅花 - v += dfs(a, b, c + 1, d, x, y) * (13 - c) / rst; - if (d < 13) //方块有剩余,可能选出的是方块 - v += dfs(a, b, c, d + 1, x, y) * (13 - d) / rst; - - //如果小王没有被选出 - if (x == 0) - v += min(min(dfs(a, b, c, d, 1, y), dfs(a, b, c, d, 2, y)), min(dfs(a, b, c, d, 3, y), dfs(a, b, c, d, 4, y))) / rst; - - //如果大王没有被选出 - if (y == 0) - v += min(min(dfs(a, b, c, d, x, 1), dfs(a, b, c, d, x, 2)), min(dfs(a, b, c, d, x, 3), dfs(a, b, c, d, x, 4))) / rst; - - return f[a][b][c][d][x][y] = v; -} - -int main() { - cin >> A >> B >> C >> D; - //① 终点状态不唯一,起点是唯的的,所以以起点为终点,以终点为起点,反着推 - //② AcWing 217. 绿豆蛙的归宿 需要建图,本题不用建图 - double res = dfs(0, 0, 0, 0, 0, 0); //四种花色、大小王都还没有被抽取 - - if (res > INF / 2) //因为是浮点数,不能用等号判断是不是相等,简单的办法就是INF/2 - puts("-1.000"); - else - printf("%.3f\n", res); - return 0; -} -``` - -### 四、期望值为什么初始化为$1$? - -$f[i]$: 从$i$卡牌状态到终点状态所需要的**期望卡牌数** - -每次抽一张牌变到下个状态,所以每条路径的权值为$1$ - -$$\large f[v]=p_1×(f[1]+1)+p_2×(f[2]+1)+p_3×(f[3]+1)+…+p_k×(f[k]+1) = \\ -\sum_{i=1}^{k}p_i+\sum_{i=1}^{k}p_i \times f[i] -$$ - - 因为$v$一定能到达下个局面,所以下个状态的概率和为$1$,这里的$\large \displaystyle \sum_{i=1}^{k}p_i=1$ 那么就有:$\displaystyle \large f[v]=1+\sum_{i=1}^{k}p_i \times f[i]$  -综上这里的$f[v]$可以初始化为$1$! -
\ No newline at end of file diff --git a/TangDou/AcWing/Math/QiWang/218_dp.cpp b/TangDou/AcWing/Math/QiWang/218_dp.cpp deleted file mode 100644 index ca614d3..0000000 --- a/TangDou/AcWing/Math/QiWang/218_dp.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include -using namespace std; -const int INF = 0x3f3f3f3f; -const int N = 15; -double f[N][N][N][N][5][5]; -int a, b, c, d; -int main() { - memset(f, -1, sizeof f); - cin >> a >> b >> c >> d; - - for (int i = 13; i >= 0; i--) - for (int j = 13; j >= 0; j--) - for (int k = 13; k >= 0; k--) - for (int w = 13; w >= 0; w--) - for (int x = 4; x >= 0; x--) - for (int y = 4; y >= 0; y--) { - double &v = f[i][j][k][w][x][y]; - if (i + (x == 1) + (y == 1) >= a && j + (x == 2) + (y == 2) >= b - && k + (x == 3) + (y == 3) >= c && w + (x == 4) + (y == 4) >= d) { - v = 0; - continue; - } - - v = 1; - int sum = i + j + k + w + (x != 0) + (y != 0); - if (i < 13) v += f[i + 1][j][k][w][x][y] * (13 - i) / (54 - sum); - if (j < 13) v += f[i][j + 1][k][w][x][y] * (13 - j) / (54 - sum); - if (k < 13) v += f[i][j][k + 1][w][x][y] * (13 - k) / (54 - sum); - if (w < 13) v += f[i][j][k][w + 1][x][y] * (13 - w) / (54 - sum); - if (x == 0) { - double t = INF; - for (int u = 1; u <= 4; u++) t = min(t, f[i][j][k][w][u][y] / (54 - sum)); - v += t; - } - if (y == 0) { - double t = INF; - for (int u = 1; u <= 4; u++) t = min(t, f[i][j][k][w][x][u] / (54 - sum)); - v += t; - } - } - - if (f[0][0][0][0][0][0] > 54) - printf("-1.000"); - else - printf("%.3lf", f[0][0][0][0][0][0]); - - return 0; -} \ No newline at end of file diff --git a/TangDou/AcWing/Math/QiWang/218_dfs.cpp b/TangDou/AcWing_TiGao/T5/QiWang/218.cpp similarity index 100% rename from TangDou/AcWing/Math/QiWang/218_dfs.cpp rename to TangDou/AcWing_TiGao/T5/QiWang/218.cpp diff --git a/TangDou/AcWing_TiGao/T5/QiWang/218_dfs.cpp b/TangDou/AcWing_TiGao/T5/QiWang/218_dfs.cpp deleted file mode 100644 index cb9302e..0000000 --- a/TangDou/AcWing_TiGao/T5/QiWang/218_dfs.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include -using namespace std; -const int N = 15; -const int INF = 0x3f3f3f3f; -double f[N][N][N][N][5][5]; -int st[N][N][N][N][5][5]; -int A, B, C, D; - -//如果大小王翻出来放1里,则a++,放2里b++,... -void add(int &a, int &b, int &c, int &d, int x) { - if (x == 1) a++; - if (x == 2) b++; - if (x == 3) c++; - if (x == 4) d++; -} - -/* -功能:计算当前状态f(a,b,c,d,x,y)下的期望值 -*/ -double dfs(int a, int b, int c, int d, int x, int y) { - //记忆化,同时因为f为double类型,不能使用传统的memset(0x3f)之类 - //进行初始化并判断是否修改过,只能再开一个st数组 - if (st[a][b][c][d][x][y]) return f[a][b][c][d][x][y]; - st[a][b][c][d][x][y] = 1; - - //递归出口:当前状态是否到达目标状态,目标状态的期望值是0 - int ta = a, tb = b, tc = c, td = d; //抄出来 - add(ta, tb, tc, td, x), add(ta, tb, tc, td, y); //大王小王会改变四个花色的数量 - if (ta >= A && tb >= B && tc >= C && td >= D) return 0; - - //当前状态下的剩余牌数量 - int rst = 54 - ta - tb - tc - td; - if (rst == 0) return INF; //还没有完成目标,没有剩余的牌了,无解 - - //当前状态可以向哪些状态转移 - // Q:v为什么要初始化为1? - // A:看题解内容 - double v = 1; - if (a < 13) //黑桃有剩余,可能选出的是黑桃 - v += dfs(a + 1, b, c, d, x, y) * (13 - a) / rst; - if (b < 13) //红桃有剩余,可能选出的是红桃 - v += dfs(a, b + 1, c, d, x, y) * (13 - b) / rst; - if (c < 13) //梅花有剩余,可能选出的是梅花 - v += dfs(a, b, c + 1, d, x, y) * (13 - c) / rst; - if (d < 13) //方块有剩余,可能选出的是方块 - v += dfs(a, b, c, d + 1, x, y) * (13 - d) / rst; - - //如果小王没有被选出 - if (x == 0) - v += min(min(dfs(a, b, c, d, 1, y), dfs(a, b, c, d, 2, y)), min(dfs(a, b, c, d, 3, y), dfs(a, b, c, d, 4, y))) / rst; - - //如果大王没有被选出 - if (y == 0) - v += min(min(dfs(a, b, c, d, x, 1), dfs(a, b, c, d, x, 2)), min(dfs(a, b, c, d, x, 3), dfs(a, b, c, d, x, 4))) / rst; - - return f[a][b][c][d][x][y] = v; -} - -int main() { - cin >> A >> B >> C >> D; - //① 终点状态不唯一,起点是唯的的,所以以起点为终点,以终点为起点,反着推 - //② AcWing 217. 绿豆蛙的归宿 需要建图,本题不用建图 - double res = dfs(0, 0, 0, 0, 0, 0); //四种花色、大小王都还没有被抽取 - - if (res > INF / 2) //因为是浮点数,不能用等号判断是不是相等,简单的办法就是INF/2 - puts("-1.000"); - else - printf("%.3f\n", res); - return 0; -} \ No newline at end of file diff --git a/TangDou/AcWing_TiGao/T5/QiWang/218_dp.cpp b/TangDou/AcWing_TiGao/T5/QiWang/218_dp.cpp deleted file mode 100644 index ca614d3..0000000 --- a/TangDou/AcWing_TiGao/T5/QiWang/218_dp.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include -using namespace std; -const int INF = 0x3f3f3f3f; -const int N = 15; -double f[N][N][N][N][5][5]; -int a, b, c, d; -int main() { - memset(f, -1, sizeof f); - cin >> a >> b >> c >> d; - - for (int i = 13; i >= 0; i--) - for (int j = 13; j >= 0; j--) - for (int k = 13; k >= 0; k--) - for (int w = 13; w >= 0; w--) - for (int x = 4; x >= 0; x--) - for (int y = 4; y >= 0; y--) { - double &v = f[i][j][k][w][x][y]; - if (i + (x == 1) + (y == 1) >= a && j + (x == 2) + (y == 2) >= b - && k + (x == 3) + (y == 3) >= c && w + (x == 4) + (y == 4) >= d) { - v = 0; - continue; - } - - v = 1; - int sum = i + j + k + w + (x != 0) + (y != 0); - if (i < 13) v += f[i + 1][j][k][w][x][y] * (13 - i) / (54 - sum); - if (j < 13) v += f[i][j + 1][k][w][x][y] * (13 - j) / (54 - sum); - if (k < 13) v += f[i][j][k + 1][w][x][y] * (13 - k) / (54 - sum); - if (w < 13) v += f[i][j][k][w + 1][x][y] * (13 - w) / (54 - sum); - if (x == 0) { - double t = INF; - for (int u = 1; u <= 4; u++) t = min(t, f[i][j][k][w][u][y] / (54 - sum)); - v += t; - } - if (y == 0) { - double t = INF; - for (int u = 1; u <= 4; u++) t = min(t, f[i][j][k][w][x][u] / (54 - sum)); - v += t; - } - } - - if (f[0][0][0][0][0][0] > 54) - printf("-1.000"); - else - printf("%.3lf", f[0][0][0][0][0][0]); - - return 0; -} \ No newline at end of file From ec00ad86512665560b0578a4bb38b7dd1502496b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Mon, 25 Dec 2023 07:59:17 +0800 Subject: [PATCH 02/87] 'commit' --- TangDou/AcWing_TiGao/T5/QiWang/218.cpp | 42 +++++++++++++------------- TangDou/AcWing_TiGao/T5/QiWang/218.md | 4 --- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/QiWang/218.cpp b/TangDou/AcWing_TiGao/T5/QiWang/218.cpp index cb9302e..6c93b5a 100644 --- a/TangDou/AcWing_TiGao/T5/QiWang/218.cpp +++ b/TangDou/AcWing_TiGao/T5/QiWang/218.cpp @@ -6,7 +6,7 @@ double f[N][N][N][N][5][5]; int st[N][N][N][N][5][5]; int A, B, C, D; -//如果大小王翻出来放1里,则a++,放2里b++,... +// 如果大小王翻出来放1里,则a++,放2里b++,... void add(int &a, int &b, int &c, int &d, int x) { if (x == 1) a++; if (x == 2) b++; @@ -18,38 +18,38 @@ void add(int &a, int &b, int &c, int &d, int x) { 功能:计算当前状态f(a,b,c,d,x,y)下的期望值 */ double dfs(int a, int b, int c, int d, int x, int y) { - //记忆化,同时因为f为double类型,不能使用传统的memset(0x3f)之类 - //进行初始化并判断是否修改过,只能再开一个st数组 + // 记忆化,同时因为f为double类型,不能使用传统的memset(0x3f)之类 + // 进行初始化并判断是否修改过,只能再开一个st数组 if (st[a][b][c][d][x][y]) return f[a][b][c][d][x][y]; st[a][b][c][d][x][y] = 1; - //递归出口:当前状态是否到达目标状态,目标状态的期望值是0 - int ta = a, tb = b, tc = c, td = d; //抄出来 - add(ta, tb, tc, td, x), add(ta, tb, tc, td, y); //大王小王会改变四个花色的数量 + // 递归出口:当前状态是否到达目标状态,目标状态的期望值是0 + int ta = a, tb = b, tc = c, td = d; // 抄出来 + add(ta, tb, tc, td, x), add(ta, tb, tc, td, y); // 大王小王会改变四个花色的数量 if (ta >= A && tb >= B && tc >= C && td >= D) return 0; - //当前状态下的剩余牌数量 + // 当前状态下的剩余牌数量 int rst = 54 - ta - tb - tc - td; - if (rst == 0) return INF; //还没有完成目标,没有剩余的牌了,无解 + if (rst == 0) return INF; // 还没有完成目标,没有剩余的牌了,无解 - //当前状态可以向哪些状态转移 - // Q:v为什么要初始化为1? - // A:看题解内容 + // 当前状态可以向哪些状态转移 + // Q:v为什么要初始化为1? + // A:看题解内容 double v = 1; - if (a < 13) //黑桃有剩余,可能选出的是黑桃 + if (a < 13) // 黑桃有剩余,可能选出的是黑桃 v += dfs(a + 1, b, c, d, x, y) * (13 - a) / rst; - if (b < 13) //红桃有剩余,可能选出的是红桃 + if (b < 13) // 红桃有剩余,可能选出的是红桃 v += dfs(a, b + 1, c, d, x, y) * (13 - b) / rst; - if (c < 13) //梅花有剩余,可能选出的是梅花 + if (c < 13) // 梅花有剩余,可能选出的是梅花 v += dfs(a, b, c + 1, d, x, y) * (13 - c) / rst; - if (d < 13) //方块有剩余,可能选出的是方块 + if (d < 13) // 方块有剩余,可能选出的是方块 v += dfs(a, b, c, d + 1, x, y) * (13 - d) / rst; - //如果小王没有被选出 + // 如果小王没有被选出 if (x == 0) v += min(min(dfs(a, b, c, d, 1, y), dfs(a, b, c, d, 2, y)), min(dfs(a, b, c, d, 3, y), dfs(a, b, c, d, 4, y))) / rst; - //如果大王没有被选出 + // 如果大王没有被选出 if (y == 0) v += min(min(dfs(a, b, c, d, x, 1), dfs(a, b, c, d, x, 2)), min(dfs(a, b, c, d, x, 3), dfs(a, b, c, d, x, 4))) / rst; @@ -58,11 +58,11 @@ double dfs(int a, int b, int c, int d, int x, int y) { int main() { cin >> A >> B >> C >> D; - //① 终点状态不唯一,起点是唯的的,所以以起点为终点,以终点为起点,反着推 - //② AcWing 217. 绿豆蛙的归宿 需要建图,本题不用建图 - double res = dfs(0, 0, 0, 0, 0, 0); //四种花色、大小王都还没有被抽取 + // ① 起点状态不唯一,终点是唯一的,所以以起点为终点,以终点为起点,反着推 + // ② AcWing 217. 绿豆蛙的归宿 需要建图,本题转移关系清晰,不用建图 + double res = dfs(0, 0, 0, 0, 0, 0); // 四种花色、大小王都还没有被抽取 - if (res > INF / 2) //因为是浮点数,不能用等号判断是不是相等,简单的办法就是INF/2 + if (res > INF / 2) // 因为是浮点数,不能用等号判断是不是相等,简单的办法就是INF/2 puts("-1.000"); else printf("%.3f\n", res); diff --git a/TangDou/AcWing_TiGao/T5/QiWang/218.md b/TangDou/AcWing_TiGao/T5/QiWang/218.md index a4c716a..f230d1b 100644 --- a/TangDou/AcWing_TiGao/T5/QiWang/218.md +++ b/TangDou/AcWing_TiGao/T5/QiWang/218.md @@ -38,10 +38,6 @@ $0≤A,B,C,D≤15$ ### 二、题意分析 -$Q$:为什么从终止状态向起始状态递推? - -**答**:满足条件的终止状态较多,而起始状态唯一。考虑以终止状态为初值,起始状态为目标,进行动态规划。 - #### 状态表示 $f[a][b][c][d][x][y]$ : 当前已翻开状态下,还需翻开牌的数量 **期望数**。 From e6fa8bbec06621974b1942978a0eb197f4f76a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Mon, 25 Dec 2023 11:22:25 +0800 Subject: [PATCH 03/87] 'commit' --- TangDou/AcWing_TiGao/T5/QiWang/218.cpp | 20 +++---- TangDou/AcWing_TiGao/T5/QiWang/218.md | 72 ++++++++++++++------------ 2 files changed, 47 insertions(+), 45 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/QiWang/218.cpp b/TangDou/AcWing_TiGao/T5/QiWang/218.cpp index 6c93b5a..65bca6b 100644 --- a/TangDou/AcWing_TiGao/T5/QiWang/218.cpp +++ b/TangDou/AcWing_TiGao/T5/QiWang/218.cpp @@ -3,7 +3,6 @@ using namespace std; const int N = 15; const int INF = 0x3f3f3f3f; double f[N][N][N][N][5][5]; -int st[N][N][N][N][5][5]; int A, B, C, D; // 如果大小王翻出来放1里,则a++,放2里b++,... @@ -18,23 +17,19 @@ void add(int &a, int &b, int &c, int &d, int x) { 功能:计算当前状态f(a,b,c,d,x,y)下的期望值 */ double dfs(int a, int b, int c, int d, int x, int y) { - // 记忆化,同时因为f为double类型,不能使用传统的memset(0x3f)之类 - // 进行初始化并判断是否修改过,只能再开一个st数组 - if (st[a][b][c][d][x][y]) return f[a][b][c][d][x][y]; - st[a][b][c][d][x][y] = 1; + // 记忆化搜索 + if (f[a][b][c][d][x][y] > 0) return f[a][b][c][d][x][y]; // 递归出口:当前状态是否到达目标状态,目标状态的期望值是0 - int ta = a, tb = b, tc = c, td = d; // 抄出来 - add(ta, tb, tc, td, x), add(ta, tb, tc, td, y); // 大王小王会改变四个花色的数量 - if (ta >= A && tb >= B && tc >= C && td >= D) return 0; + int ta = a, tb = b, tc = c, td = d; // 抄出来 + add(ta, tb, tc, td, x), add(ta, tb, tc, td, y); // 大王小王会改变四个花色的数量 + if (ta >= A && tb >= B && tc >= C && td >= D) return 0; // 如果条件全满足就是终止状态 // 当前状态下的剩余牌数量 int rst = 54 - ta - tb - tc - td; - if (rst == 0) return INF; // 还没有完成目标,没有剩余的牌了,无解 + if (rst <= 0) return INF; // 还没有完成目标,没有剩余的牌了,无解 // 当前状态可以向哪些状态转移 - // Q:v为什么要初始化为1? - // A:看题解内容 double v = 1; if (a < 13) // 黑桃有剩余,可能选出的是黑桃 v += dfs(a + 1, b, c, d, x, y) * (13 - a) / rst; @@ -58,8 +53,7 @@ double dfs(int a, int b, int c, int d, int x, int y) { int main() { cin >> A >> B >> C >> D; - // ① 起点状态不唯一,终点是唯一的,所以以起点为终点,以终点为起点,反着推 - // ② AcWing 217. 绿豆蛙的归宿 需要建图,本题转移关系清晰,不用建图 + double res = dfs(0, 0, 0, 0, 0, 0); // 四种花色、大小王都还没有被抽取 if (res > INF / 2) // 因为是浮点数,不能用等号判断是不是相等,简单的办法就是INF/2 diff --git a/TangDou/AcWing_TiGao/T5/QiWang/218.md b/TangDou/AcWing_TiGao/T5/QiWang/218.md index f230d1b..22c5065 100644 --- a/TangDou/AcWing_TiGao/T5/QiWang/218.md +++ b/TangDou/AcWing_TiGao/T5/QiWang/218.md @@ -13,6 +13,18 @@ $Rainbow$ 想问问 $Admin$,得到 $A$ 张黑桃、$B$ 张红桃、$C$ 张梅 特殊地,如果翻开的牌是大王或者小王,$Admin$ 将会把它作为某种花色的牌放入对应堆中,使得放入之后 $E$的值尽可能小。 +> 注:从牌堆里面翻出来的牌的期望张数最少是多少? +> **期望**:用初等数学的语言去描述,就是平均值是多少。 +> $Q:$为什么会有最少的概念出现的呢?这个数量不是固定的吗? +> 答:这是因为大王和小王可以被认为是其中四个花色中的某一种花色,这就导致产生了不同的事件。 +> 放到红桃里是一种事件,放到黑桃里是一种事件,...,这四个事件的期望不一定相同! +> 选择就在大小王上,我们要选择一个期望张数最少的放法,问我们这个期望张数最小是多少。 + +###$yxc$经典语录 +> **图中,点是状态表示,边是状态转移**。 +> **理解**:前一个题中,需要建图,建图完成后才能用图进行状态表示和状态转移,本题,状态表示和状态转移都是很明确的,不需要建图。 +> **总结** :动态规划是精髓,建图与否是表象。 + 由于 $Admin$ 和 $Rainbow$ 还在玩扑克,所以这个程序就交给你来写了。 **输入格式** @@ -66,10 +78,9 @@ using namespace std; const int N = 15; const int INF = 0x3f3f3f3f; double f[N][N][N][N][5][5]; -int st[N][N][N][N][5][5]; int A, B, C, D; -//如果大小王翻出来放1里,则a++,放2里b++,... +// 如果大小王翻出来放1里,则a++,放2里b++,... void add(int &a, int &b, int &c, int &d, int x) { if (x == 1) a++; if (x == 2) b++; @@ -81,38 +92,34 @@ void add(int &a, int &b, int &c, int &d, int x) { 功能:计算当前状态f(a,b,c,d,x,y)下的期望值 */ double dfs(int a, int b, int c, int d, int x, int y) { - //记忆化,同时因为f为double类型,不能使用传统的memset(0x3f)之类 - //进行初始化并判断是否修改过,只能再开一个st数组 - if (st[a][b][c][d][x][y]) return f[a][b][c][d][x][y]; - st[a][b][c][d][x][y] = 1; + // 记忆化搜索 + if (f[a][b][c][d][x][y] > 0) return f[a][b][c][d][x][y]; - //递归出口:当前状态是否到达目标状态,目标状态的期望值是0 - int ta = a, tb = b, tc = c, td = d; //抄出来 - add(ta, tb, tc, td, x), add(ta, tb, tc, td, y); //大王小王会改变四个花色的数量 - if (ta >= A && tb >= B && tc >= C && td >= D) return 0; + // 递归出口:当前状态是否到达目标状态,目标状态的期望值是0 + int ta = a, tb = b, tc = c, td = d; // 抄出来 + add(ta, tb, tc, td, x), add(ta, tb, tc, td, y); // 大王小王会改变四个花色的数量 + if (ta >= A && tb >= B && tc >= C && td >= D) return 0; // 如果条件全满足就是终止状态 - //当前状态下的剩余牌数量 + // 当前状态下的剩余牌数量 int rst = 54 - ta - tb - tc - td; - if (rst == 0) return INF; //还没有完成目标,没有剩余的牌了,无解 + if (rst <= 0) return INF; // 还没有完成目标,没有剩余的牌了,无解 - //当前状态可以向哪些状态转移 - // Q:v为什么要初始化为1? - // A:看题解内容 + // 当前状态可以向哪些状态转移 double v = 1; - if (a < 13) //黑桃有剩余,可能选出的是黑桃 + if (a < 13) // 黑桃有剩余,可能选出的是黑桃 v += dfs(a + 1, b, c, d, x, y) * (13 - a) / rst; - if (b < 13) //红桃有剩余,可能选出的是红桃 + if (b < 13) // 红桃有剩余,可能选出的是红桃 v += dfs(a, b + 1, c, d, x, y) * (13 - b) / rst; - if (c < 13) //梅花有剩余,可能选出的是梅花 + if (c < 13) // 梅花有剩余,可能选出的是梅花 v += dfs(a, b, c + 1, d, x, y) * (13 - c) / rst; - if (d < 13) //方块有剩余,可能选出的是方块 + if (d < 13) // 方块有剩余,可能选出的是方块 v += dfs(a, b, c, d + 1, x, y) * (13 - d) / rst; - //如果小王没有被选出 + // 如果小王没有被选出 if (x == 0) v += min(min(dfs(a, b, c, d, 1, y), dfs(a, b, c, d, 2, y)), min(dfs(a, b, c, d, 3, y), dfs(a, b, c, d, 4, y))) / rst; - //如果大王没有被选出 + // 如果大王没有被选出 if (y == 0) v += min(min(dfs(a, b, c, d, x, 1), dfs(a, b, c, d, x, 2)), min(dfs(a, b, c, d, x, 3), dfs(a, b, c, d, x, 4))) / rst; @@ -121,11 +128,10 @@ double dfs(int a, int b, int c, int d, int x, int y) { int main() { cin >> A >> B >> C >> D; - //① 终点状态不唯一,起点是唯的的,所以以起点为终点,以终点为起点,反着推 - //② AcWing 217. 绿豆蛙的归宿 需要建图,本题不用建图 - double res = dfs(0, 0, 0, 0, 0, 0); //四种花色、大小王都还没有被抽取 - if (res > INF / 2) //因为是浮点数,不能用等号判断是不是相等,简单的办法就是INF/2 + double res = dfs(0, 0, 0, 0, 0, 0); // 四种花色、大小王都还没有被抽取 + + if (res > INF / 2) // 因为是浮点数,不能用等号判断是不是相等,简单的办法就是INF/2 puts("-1.000"); else printf("%.3f\n", res); @@ -133,16 +139,18 @@ int main() { } ``` -### 四、期望值为什么初始化为$1$? +#### $Q$:期望值为什么初始化为$1$? -$f[i]$: 从$i$卡牌状态到终点状态所需要的**期望卡牌数** +
+ +$f[v_i]$: 从$i$卡牌状态到终点状态所需要的**期望卡牌数** 每次抽一张牌变到下个状态,所以每条路径的权值为$1$ -$$\large f[v]=p_1×(f[1]+1)+p_2×(f[2]+1)+p_3×(f[3]+1)+…+p_k×(f[k]+1) = \\ -\sum_{i=1}^{k}p_i+\sum_{i=1}^{k}p_i \times f[i] +$$\large f[v]=p_1×(f[v_1]+1)+p_2×(f[v_2]+1)+p_3×(f[v_3]+1)+…+p_k×(f[v_k]+1) = \\ +\sum_{i=1}^{k}p_i+\sum_{i=1}^{k}p_i \times f[v_i] $$ - 因为$v$一定能到达下个局面,所以下个状态的概率和为$1$,这里的$\large \displaystyle \sum_{i=1}^{k}p_i=1$ 那么就有:$\displaystyle \large f[v]=1+\sum_{i=1}^{k}p_i \times f[i]$  -综上这里的$f[v]$可以初始化为$1$! -
\ No newline at end of file + 因为$v$一定能到达下个局面,所以下个状态的概率和为$1$,这里的$\large \displaystyle \sum_{i=1}^{k}p_i=1$ 那么就有:$\displaystyle \large f[v]=1+\sum_{i=1}^{k}p_i \times f[v_i]$  +综上这里的$f[v]$可以初始化为$1$。 + From 699ad726e3c095f0cd96e12d792cb45aaa73def9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Mon, 25 Dec 2023 13:07:45 +0800 Subject: [PATCH 04/87] 'commit' --- .../Math => AcWing_TiGao/T5}/GameTheory/1319.cpp | 0 .../Math => AcWing_TiGao/T5}/GameTheory/1319.md | 7 ++++--- .../Math => AcWing_TiGao/T5}/GameTheory/1321.md | 0 .../Math => AcWing_TiGao/T5}/GameTheory/1321_1.cpp | 0 .../Math => AcWing_TiGao/T5}/GameTheory/1321_2.cpp | 0 .../Math => AcWing_TiGao/T5}/GameTheory/1322.cpp | 0 .../Math => AcWing_TiGao/T5}/GameTheory/1322.md | 0 .../Math => AcWing_TiGao/T5}/GameTheory/1322.vsdx | Bin .../Math => AcWing_TiGao/T5}/GameTheory/HDU1730.cpp | 0 .../Math => AcWing_TiGao/T5}/GameTheory/HDU1730.md | 0 .../Math => AcWing_TiGao/T5}/GameTheory/HDU1846.cpp | 0 .../Math => AcWing_TiGao/T5}/GameTheory/HDU1847.cpp | 0 .../Math => AcWing_TiGao/T5}/GameTheory/HDU1850.cpp | 0 .../Math => AcWing_TiGao/T5}/GameTheory/HDU2176.cpp | 0 .../Math => AcWing_TiGao/T5}/GameTheory/HDU2188.cpp | 0 .../Math => AcWing_TiGao/T5}/GameTheory/HDU4315.cpp | 0 .../Math => AcWing_TiGao/T5}/GameTheory/HDU4315.md | 0 .../T5}/GameTheory/POJ1704.drawio | 0 .../Math => AcWing_TiGao/T5}/GameTheory/POJ1704.md | 0 .../T5}/GameTheory/POJ1704_Nim.cpp | 0 .../T5}/GameTheory/POJ1704_StaircaseNim_7.cpp | 0 .../T5}/GameTheory/POJ1704_StaircaseNim_8.cpp | 0 .../Math => AcWing_TiGao/T5}/GameTheory/test.cpp | 0 .../T5}/GameTheory/不一样的博弈论笔记.md | 0 .../T5}/GameTheory/博弈论.md | 0 25 files changed, 4 insertions(+), 3 deletions(-) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/1319.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/1319.md (89%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/1321.md (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/1321_1.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/1321_2.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/1322.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/1322.md (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/1322.vsdx (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/HDU1730.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/HDU1730.md (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/HDU1846.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/HDU1847.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/HDU1850.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/HDU2176.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/HDU2188.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/HDU4315.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/HDU4315.md (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/POJ1704.drawio (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/POJ1704.md (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/POJ1704_Nim.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/POJ1704_StaircaseNim_7.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/POJ1704_StaircaseNim_8.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/test.cpp (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/不一样的博弈论笔记.md (100%) rename TangDou/{AcWing/Math => AcWing_TiGao/T5}/GameTheory/博弈论.md (100%) diff --git a/TangDou/AcWing/Math/GameTheory/1319.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1319.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/1319.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/1319.cpp diff --git a/TangDou/AcWing/Math/GameTheory/1319.md b/TangDou/AcWing_TiGao/T5/GameTheory/1319.md similarity index 89% rename from TangDou/AcWing/Math/GameTheory/1319.md rename to TangDou/AcWing_TiGao/T5/GameTheory/1319.md index 0e8c8c4..17ae03b 100644 --- a/TangDou/AcWing/Math/GameTheory/1319.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1319.md @@ -47,15 +47,16 @@ win 例:$mex({1,2})=0,mex({0,1})=2,mex({0,1,2,4})=3$ -在一张有向无环图中,对于每个点 $u$,设其**所有能到的点**的 $SG$ 函数值集合为集合 $A$,那么 $u$ 的 $SG$ 函数值为 $mex(A)$,记做 $SG(u)=mex(A)$ -例图: +在一张有向无环图中,对于每个点 $u$,设其 **所有能到的点** 的 $SG$ 函数值集合为集合 $A$,那么 $u$ 的 $SG$ 函数值为 $mex(A)$,记做 $SG(u)=mex(A)$ + +如图:
例图解释: $SG(5)=mex({\phi})=0$ $SG(3)=mex({SG(5)})=mex({0})=1$ $SG(4)=mex({SG(5),SG(3)})=mex({0,1})=2$ -$SG(2)=mex({SG(3)}=mex({1})=0$ +$SG(2)=mex({SG(3)})=mex({1})=0$ $SG(1)=mex({SG(2),SG(4)})=mex({0,2})=1$ #### 本题思路 diff --git a/TangDou/AcWing/Math/GameTheory/1321.md b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md similarity index 100% rename from TangDou/AcWing/Math/GameTheory/1321.md rename to TangDou/AcWing_TiGao/T5/GameTheory/1321.md diff --git a/TangDou/AcWing/Math/GameTheory/1321_1.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1321_1.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/1321_1.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/1321_1.cpp diff --git a/TangDou/AcWing/Math/GameTheory/1321_2.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1321_2.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/1321_2.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/1321_2.cpp diff --git a/TangDou/AcWing/Math/GameTheory/1322.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/1322.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp diff --git a/TangDou/AcWing/Math/GameTheory/1322.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md similarity index 100% rename from TangDou/AcWing/Math/GameTheory/1322.md rename to TangDou/AcWing_TiGao/T5/GameTheory/1322.md diff --git a/TangDou/AcWing/Math/GameTheory/1322.vsdx b/TangDou/AcWing_TiGao/T5/GameTheory/1322.vsdx similarity index 100% rename from TangDou/AcWing/Math/GameTheory/1322.vsdx rename to TangDou/AcWing_TiGao/T5/GameTheory/1322.vsdx diff --git a/TangDou/AcWing/Math/GameTheory/HDU1730.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/HDU1730.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/HDU1730.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/HDU1730.cpp diff --git a/TangDou/AcWing/Math/GameTheory/HDU1730.md b/TangDou/AcWing_TiGao/T5/GameTheory/HDU1730.md similarity index 100% rename from TangDou/AcWing/Math/GameTheory/HDU1730.md rename to TangDou/AcWing_TiGao/T5/GameTheory/HDU1730.md diff --git a/TangDou/AcWing/Math/GameTheory/HDU1846.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/HDU1846.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/HDU1846.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/HDU1846.cpp diff --git a/TangDou/AcWing/Math/GameTheory/HDU1847.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/HDU1847.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/HDU1847.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/HDU1847.cpp diff --git a/TangDou/AcWing/Math/GameTheory/HDU1850.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/HDU1850.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/HDU1850.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/HDU1850.cpp diff --git a/TangDou/AcWing/Math/GameTheory/HDU2176.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/HDU2176.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/HDU2176.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/HDU2176.cpp diff --git a/TangDou/AcWing/Math/GameTheory/HDU2188.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/HDU2188.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/HDU2188.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/HDU2188.cpp diff --git a/TangDou/AcWing/Math/GameTheory/HDU4315.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/HDU4315.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/HDU4315.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/HDU4315.cpp diff --git a/TangDou/AcWing/Math/GameTheory/HDU4315.md b/TangDou/AcWing_TiGao/T5/GameTheory/HDU4315.md similarity index 100% rename from TangDou/AcWing/Math/GameTheory/HDU4315.md rename to TangDou/AcWing_TiGao/T5/GameTheory/HDU4315.md diff --git a/TangDou/AcWing/Math/GameTheory/POJ1704.drawio b/TangDou/AcWing_TiGao/T5/GameTheory/POJ1704.drawio similarity index 100% rename from TangDou/AcWing/Math/GameTheory/POJ1704.drawio rename to TangDou/AcWing_TiGao/T5/GameTheory/POJ1704.drawio diff --git a/TangDou/AcWing/Math/GameTheory/POJ1704.md b/TangDou/AcWing_TiGao/T5/GameTheory/POJ1704.md similarity index 100% rename from TangDou/AcWing/Math/GameTheory/POJ1704.md rename to TangDou/AcWing_TiGao/T5/GameTheory/POJ1704.md diff --git a/TangDou/AcWing/Math/GameTheory/POJ1704_Nim.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/POJ1704_Nim.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/POJ1704_Nim.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/POJ1704_Nim.cpp diff --git a/TangDou/AcWing/Math/GameTheory/POJ1704_StaircaseNim_7.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/POJ1704_StaircaseNim_7.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/POJ1704_StaircaseNim_7.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/POJ1704_StaircaseNim_7.cpp diff --git a/TangDou/AcWing/Math/GameTheory/POJ1704_StaircaseNim_8.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/POJ1704_StaircaseNim_8.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/POJ1704_StaircaseNim_8.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/POJ1704_StaircaseNim_8.cpp diff --git a/TangDou/AcWing/Math/GameTheory/test.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/test.cpp similarity index 100% rename from TangDou/AcWing/Math/GameTheory/test.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/test.cpp diff --git a/TangDou/AcWing/Math/GameTheory/不一样的博弈论笔记.md b/TangDou/AcWing_TiGao/T5/GameTheory/不一样的博弈论笔记.md similarity index 100% rename from TangDou/AcWing/Math/GameTheory/不一样的博弈论笔记.md rename to TangDou/AcWing_TiGao/T5/GameTheory/不一样的博弈论笔记.md diff --git a/TangDou/AcWing/Math/GameTheory/博弈论.md b/TangDou/AcWing_TiGao/T5/GameTheory/博弈论.md similarity index 100% rename from TangDou/AcWing/Math/GameTheory/博弈论.md rename to TangDou/AcWing_TiGao/T5/GameTheory/博弈论.md From 234b2167c17a4a66cdf69c33e10315073e5f0b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Mon, 25 Dec 2023 14:12:53 +0800 Subject: [PATCH 05/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1319.md | 49 +++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1319.md b/TangDou/AcWing_TiGao/T5/GameTheory/1319.md index 17ae03b..a2adede 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1319.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1319.md @@ -43,6 +43,7 @@ win ### 二、解题思路 +#### 1、$SG$函数 首先定义 $mex$ 函数,这是施加于一个集合的函数,返回 **最小的不属于这个集合的非负整数** 例:$mex({1,2})=0,mex({0,1})=2,mex({0,1,2,4})=3$ @@ -59,7 +60,53 @@ $SG(4)=mex({SG(5),SG(3)})=mex({0,1})=2$ $SG(2)=mex({SG(3)})=mex({1})=0$ $SG(1)=mex({SG(2),SG(4)})=mex({0,2})=1$ -#### 本题思路 +#### 2、本题和 $SG$ 函数有什么关系? +下面先说本题做法,再证明该方法正确性。 + +**做法**:求出每个棋子所在的点的 $SG$ 函数值,将所有值异或起来。若异或值不为 $0$,则输出$win$,否则输出$lose$ + +**证明**: +首先,由于这是一张有向无环图,所以游戏最后一定会结束,也就是说每个棋子最后都会移动到一个点上,且该点没有任何能到达的点。 +那么根据定义,结束状态的所有点的 $SG$ 函数值异或起来为 $0$,做法对于结束状态可行。 +所以接下来,只要证明出 + +- ① 任何一种每个棋子所在点的 $SG$ 函数值异或起来非 $0$ 的情况,一定能通过一次移动棋子,到达一个 每个棋子所在点的 $SG$ 函数值异或起来为 $0$ 的情况 + +- ② 任何一种每个棋子所在点的 $SG$ 函数值异或起来为 $0$ 的情况,一定不能通过一次移动棋子,到达一个每个棋子所在点的 $SG$ 函数值异或起来为 $0$ 的情况 + +那么做法就是对的 + + +**证明** $1$: +设每个棋子所在点的 $SG$ 函数值分别为 $a_1,a_2,⋯,a_n$ +设 $x=a_1 XOR a_2 XOR ⋯ XOR a_n$,设 $x$ 的最高位为第 $k$ 位,那么在 $a_1,a_2,⋯,a_n$ 中,一定有一个值的第 $k$ 位为 $1$。 + +设该值为 $a_i$,那么由于 $x$ 的第 $k$位和 $a_i$ 的第 $k$ 位都是 $1$,且第 $k$ 位是 $x$ 的最高位,所以 $a_i XOR x$ 一定小于 $a_i$ + +又因为 $a_i$ 是其中一个棋子所在点的 $SG$ 函数值,那么根据 $SG$ 函数值的定义,该点能到达的所有点中,一定存在一个点的 $SG$ 函数值为 $a_i XOR x$ + +那么我们就可以将该点上的棋子,移到一个 $SG$ 函数值为 $a_i XOR x$ 的点上去 + +移完之后,原来每个棋子所在点的 $SG$ 函数异或值就变为了 $a_1 XOR a_2 XOR ⋯ XOR a_{i−1} XOR (a_i XOR x) XOR a_{i+1} ⋯ XOR a_n$ + +$=(a_1 XOR a_2 XOR ⋯ XOR a_n) XOR x=x XOR x=0$ + +① 证毕 + + +**证明** $2$: +反证法,设将点 $u$ 上的棋子移动到点 $v$ 上后,每个棋子所在点的 $SG$ 函数值仍然为 $0$ +那就说明 $SG(u)=SG(v)$,不符合 $SG$ 函数的定义,不成立 +② 证毕 + +所以做法是正确的。 + +那么如何求出每个点的 $SG$ 函数值呢? +记忆化搜索就好啦~ +每层记忆化搜索中,如果该点的 $SG$ 函数值已经被计算出,那就直接返回该值。否则用一个 $set$ 记录每个点能到的所有点的 $SG$ 函数值集合,然后从 $0$ 开始遍历,找到第一个 $set$ 里面没有的数,将该值记录在该点上并返回。 + + +#### 3、回到本题 - 如果只有一个棋子(棋子位置是$s$): 先手必胜 $\Leftrightarrow $ `sg(s)!=0` From ca23979c24b00b525c4e0c8ea891f336b6df4b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Mon, 25 Dec 2023 14:26:16 +0800 Subject: [PATCH 06/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1319.md | 24 +++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1319.md b/TangDou/AcWing_TiGao/T5/GameTheory/1319.md index a2adede..c707378 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1319.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1319.md @@ -53,7 +53,8 @@ win 如图:
-例图解释: +**例图解释**: + $SG(5)=mex({\phi})=0$ $SG(3)=mex({SG(5)})=mex({0})=1$ $SG(4)=mex({SG(5),SG(3)})=mex({0,1})=2$ @@ -67,7 +68,9 @@ $SG(1)=mex({SG(2),SG(4)})=mex({0,2})=1$ **证明**: 首先,由于这是一张有向无环图,所以游戏最后一定会结束,也就是说每个棋子最后都会移动到一个点上,且该点没有任何能到达的点。 -那么根据定义,结束状态的所有点的 $SG$ 函数值异或起来为 $0$,做法对于结束状态可行。 + +根据定义,结束状态的所有点的 $SG$ 函数值异或起来为 $0$,**做法对于结束状态可行**。 + 所以接下来,只要证明出 - ① 任何一种每个棋子所在点的 $SG$ 函数值异或起来非 $0$ 的情况,一定能通过一次移动棋子,到达一个 每个棋子所在点的 $SG$ 函数值异或起来为 $0$ 的情况 @@ -79,15 +82,15 @@ $SG(1)=mex({SG(2),SG(4)})=mex({0,2})=1$ **证明** $1$: 设每个棋子所在点的 $SG$ 函数值分别为 $a_1,a_2,⋯,a_n$ -设 $x=a_1 XOR a_2 XOR ⋯ XOR a_n$,设 $x$ 的最高位为第 $k$ 位,那么在 $a_1,a_2,⋯,a_n$ 中,一定有一个值的第 $k$ 位为 $1$。 - -设该值为 $a_i$,那么由于 $x$ 的第 $k$位和 $a_i$ 的第 $k$ 位都是 $1$,且第 $k$ 位是 $x$ 的最高位,所以 $a_i XOR x$ 一定小于 $a_i$ +设 $x=a_1 XOR a_2 XOR ⋯ XOR a_n$,**此时$x$一定不等于$0$**。 +设 $x$ 的最高位为第 $k$ 位,那么在 $a_1,a_2,⋯,a_n$ 中,一定有一个值的第 $k$ 位为 $1$。 +设该值为 $a_i$,那么由于 $x$ 的第 $k$位和 $a_i$ 的第 $k$ 位都是 $1$,且,第 $k$ 位是 $x$ 的最高位,所以 $a_i XOR x$ 一定小于 $a_i$ 又因为 $a_i$ 是其中一个棋子所在点的 $SG$ 函数值,那么根据 $SG$ 函数值的定义,该点能到达的所有点中,一定存在一个点的 $SG$ 函数值为 $a_i XOR x$ 那么我们就可以将该点上的棋子,移到一个 $SG$ 函数值为 $a_i XOR x$ 的点上去 -移完之后,原来每个棋子所在点的 $SG$ 函数异或值就变为了 $a_1 XOR a_2 XOR ⋯ XOR a_{i−1} XOR (a_i XOR x) XOR a_{i+1} ⋯ XOR a_n$ +**移完之后**,原来每个棋子所在点的 $SG$ 函数异或值就变为了 $a_1 XOR a_2 XOR ⋯ XOR a_{i−1} XOR (a_i XOR x) XOR a_{i+1} ⋯ XOR a_n$ $=(a_1 XOR a_2 XOR ⋯ XOR a_n) XOR x=x XOR x=0$ @@ -101,18 +104,11 @@ $=(a_1 XOR a_2 XOR ⋯ XOR a_n) XOR x=x XOR x=0$ 所以做法是正确的。 -那么如何求出每个点的 $SG$ 函数值呢? +#### 3、如何求出每个点的 $SG$ 函数值呢? 记忆化搜索就好啦~ 每层记忆化搜索中,如果该点的 $SG$ 函数值已经被计算出,那就直接返回该值。否则用一个 $set$ 记录每个点能到的所有点的 $SG$ 函数值集合,然后从 $0$ 开始遍历,找到第一个 $set$ 里面没有的数,将该值记录在该点上并返回。 -#### 3、回到本题 -- 如果只有一个棋子(棋子位置是$s$): -先手必胜 $\Leftrightarrow $ `sg(s)!=0` - -- 存在多个棋子(其实可以看成存在多个相同的棋盘,棋子的位置是$s_1,…,s_k$ -先手必胜 $\Leftrightarrow $ `sg(s1)^sg(s2)^...^sg(sk) != 0` - ### 三、实现代码 ```cpp {.line-numbers} #include From 3b645965d726e036f29f3adcb4c14c9afb71eb44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Mon, 25 Dec 2023 15:54:46 +0800 Subject: [PATCH 07/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1321.md | 127 +++++---------------- 1 file changed, 30 insertions(+), 97 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md index 8c4deee..a72bd71 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md @@ -1,7 +1,5 @@ ##[$AcWing$ $1321$. 取石子](https://www.acwing.com/problem/content/description/1323/) -**[参考题解](https://www.cnblogs.com/ZJXXCN/p/11068490.html)** - ### 一、题目描述 $Alice$ 和 $Bob$ 两个好朋友又开始玩取石子了。 @@ -53,117 +51,52 @@ NO 必败态 $\Rightarrow$ 选择任何路线 $\Rightarrow$ 必胜态 -### 三、简单情况 -为什么会想到讨论简单情况呢?我们来思考一下:如果某一堆石子只有$1$个,随着我们执行拿走$1$个的操作,它的堆就没了,这样石子个数变了,堆数也变了,两个变量,问题变复杂了,我们上来就想难题,怕是搞不定。 - -既然这样,我们就思考一下 **子空间** :只考虑所有个数大于等于$2$的那些堆,其它可能存在石子数等于$1$的,等我们想明白这个简单问题再研究扩展的事,由易到难。 - -同时,我们需要思考博弈的胜负与什么因素相关呢?因为只有两种操作:**拿走一个石子、合并两堆**,很显然,**两个关键因素:石子个数、堆数** - -同时,两个操作同一时间只能执行一个,所以可以理解为拿走一个石子对结果影响一下,合并两堆石子对结果也是影响一下,初步考虑应该堆个数与石子总数的加法关系相关。 - +### 三、本题题解 +先来考虑 **简单情况**:所有堆的石子个数 $>1$ -**子空间:当每堆的石子个数都是大于等于$2$时** +设 $b$ = 堆数 + 石子总数 $− 1$ -
设$b$ = 堆数 + 石子总数 - $1$
-
结论:$b$是奇数⟺先手必胜,$b$是偶数⟺先手必败
+可以推出: **先手必胜 $\Leftrightarrow$ $b$是奇数** -**证明:** +**证明**:考虑以下结论: -1、边界:当我们只有一堆石子且该堆石子个数为$1$个时,$b=1$,先手必胜。 +$α$. 任意一个是奇数的情况 **可以** 被转换成一个偶数的情况 (一定存在一个偶数后继) -2、当$b$为奇数,一定可以通过某种操作将$b$变成偶数 -* 如果堆数大于$1$,合并两堆让$b$变为偶数 -* 如果堆数等于$1$,从该堆石子中取出一个就可以让$b$变为偶数 +$β$. 任意一个是偶数的情况 **一定** 会被转换成一个奇数的情况 (所有后继都是奇数)。 -3、当$b$为偶数,无论如何操作,$b$都必将变为奇数 -* 合并两堆,则$b$变为奇数 -* 从某一堆中取走一个石子: - * 若该堆石子个数大于$2$,则$b$变为奇数,且所有堆石子数量严格大于$1$ - * 若该堆石子个数等于$2$,取一个石子后,$b$变为奇数,该堆石子个数变为$1$个,此时就再是子空间范围内了,因为出现某堆的石子个数为$1$,而不是每一堆都大于等于$2$了!需要继续分类讨论: - -#### 特殊情况 -此时为了保证所有堆的石子个数大于$1$,**足够聪明的对手** 可以进行的操作分为两类: - ① 如果只有这一堆石子,此时 **对手必胜** - ② 如果有多堆石子,可以将这一个石子合并到其他堆中,这样每对石子个数都大于$1$ - - **$Q$:对手为什么一定要采用合并的操作,而不是从别的堆中取石子呢?** - 我来举两个简单的栗子: - - * **只有一堆石子** - 石子个数是$2$个。你拿走一个,对手直接拿走另一个,游戏结束,**对手赢了**!你也是足够聪明的,你会在这种情况下这么拿吗?不能吧~,啥时候可能遇到这个情况呢?就是你被 **逼到** 这个场景下,也就是一直处于必败态! - - * **两堆石子** - 每堆石子个数是$2$个。**我是先手**,可以有两种选择: - - (1)、从任意一堆中拿走$1$个, 现在的局面是$\{2,1\}$ - $$\large 后手选择(对手) \Rightarrow - \left\{\begin{matrix} - 从2中取一个 & \Rightarrow \{1,1\} & \Rightarrow - \large \left\{\begin{matrix} - 先手合并 \Rightarrow \{2\}& 剩下一个一个取,先手胜 \\ - 先手后手一个一个取 \Rightarrow 先手败 & - \end{matrix}\right. - \\ - 从1中取一个& \Rightarrow \{2,0\} & 剩下一个一个取,先手败\\ - 合并两堆 & \Rightarrow \{3\} & 剩下一个一个取,先手胜 \\ - \end{matrix}\right. - $$ - 指望对手出错我才有赢的机会,人家要是聪明,我就废了! - - 我是先手,我肯定不能把自己的命运交到别人手中!我选择合并两堆,这样我保准赢! - - (2)、把两堆直接合并,现在的状态$\{4\}$ - 这下进入了我的套路,你取吧,你取一个,我也取一个;你再取一个,我也再取一个,结果,没有了,**对手必败**。 - - 上面的例子可能不能描述所有场景,我现在$b$是奇数,我在必胜态,我不会让自己陷入到$b$可能是偶数的状态中去,如果我选择了 - - 合并操作减少$1$个堆 - - 拿走操作减少$1$个石子 - 都会把$b-1$这个偶数态给对方 - 我不会傻到一个操作,即可能造成堆也变化,让石子个数也变化,这样就得看对方怎么选择了,而他还那么聪明,我不能犯这样的错误。 +先来考虑结论 $α.$ +- 如果堆数 $>1$,可以合并其中两堆石子,这样 $b$ 就会变成偶数 +- 如果堆数 $=1$,就可以拿掉 $1$ 个石子,这样 $b$ 也会变成偶数 -### 四、本题情况 -本题中可能存在一些堆的石子个数等于$1$: -* 假设有$a$堆石子,其中每堆石子个数为$1$ -* 剩余堆的石子个数都严格大于$1$ +再来考虑结论 $β$. +- 合并其中的两堆石子:显然,$b$ 会变成奇数 +- 取了一个石子: + (1) 取得堆中石子个数 $>2$:石子个数 $− 1$,$b$ 会变成奇数 + (2) 取得堆中石子个数 $=2$:石子个数变成了 $1$(继续分情况讨论): + 1) 只有一堆:$b$ 是奇数,对手必胜 + 2) 多于一堆:$b$ 是奇数,对手可以把剩下的一个石子放到其他的堆里面去,对手同样必胜 -根据这些数量大于$1$的堆的石子可以求出上述定义出的$b$,我们使用$f(a, b)$表示此时先手必胜还是必败,因为博弈论在本质上是可以递推的,我们可以想出起点,再想出递推关系,就可以递推得到更大数据情况下的递推值,也就是博弈论本质上是$dp$。 +这样,我们成功的证明除了: **先手必胜 $\Leftrightarrow $ ($b$ 是奇数)** -
-相关疑问 -$Q1:$**情况**$3$**为什么是两个表达式?** -答: -①当右侧存在时,合并左边两堆石子,则右侧多出一堆石子,并且,石子个数增加$2$,也就是$b+=3$ +接下来考虑一般情况:有些堆的石子个数可能 $=1$ +假设石子个数 $=1$ 的堆数为 $a$,$b$ 仍然表示剩余石子的 **操作数**,即 (堆数 $+$ 石子总数 $− 1$) +把所有石子分成两个区域,一区域的数量 $=1$ (对应 $a$),二区域的数量都 $>1$ (对应 $b$). -②当右侧一个都没有的时候,左边送来了一堆,两个石子,按$b$的定义,是堆数+石子个数$-1=2$,即$b+=2$ +那么可以写出以下转移: -$Q2$:**为什么用一个奇数来描述简单情况的状态,而不是用偶数呢?** -答:因为要通过递推式进行计算,最终的边界是需要我们考虑的: - -- 如果用奇数,那么边界就是$b=1$,表示只有$1$堆,石子数量只有$1$个,此时当然必胜。 - -- 如果用偶数,比如边界是$b=0$,表示目前$0$堆,$0$个石子,这都啥也没有了,还必胜态,不符合逻辑,说不清道不明。 - -- 那要是不用$b=0$做边界,用$b=2$呢?表示只有$1$堆,石子数量只有$1$个,这个应该也是可以,但没有再仔细想了。 - -$Q3:$**情况**$2$**从右边取一个石子,如果此时右侧存在某一堆中石子个数是$2$,取走$1$个后,变成了$1$,不就是右侧减少了一个堆,减少了两个石子,即$b-=3$;同时,此堆石子个数变为$1$,左侧个数$a+=1$,为什么没有看到这个状态变化呢?** - -答:这是因为聪明人不会从右侧某个石子数量大于$2$的堆中取走石子! - -看一下 **讨论简单情况** 中第$3$点后面的 **特殊情况**: - - 如果右侧只有一堆,石子数量为$2$,拿走$1$个,剩$1$个,一堆一个,对方必胜,此为必败态 - - - 如果右侧大于一堆,某一堆只有$2$个石子,拿走$1$个,剩$1$个,对手足够聪明,会采用右侧两堆合并的办法,此时 石子数量减$1$,堆数减$1$,对$b$的影响是减$2$,对$b$的奇偶性没有影响,换句话说,如果你现在处在必败态,你这么整完,还是必败态 - - -### 五、时间复杂度 -这里因为$a$最大取$50$,$b$最大取$50050$,因此计算这些状态的计算量为$2.5×10^6$,虽然有最多$100$次查询,但是这些状态每个只会计算一遍,因此不会超时。 +$f(a,b)$: + +① 从 $a$中取 $1$个 $→f(a−1,b)$ +② 从 $b$中取 $1$个 $→f(a,b−1)$ +③ 合并 $b$中的 $2$个 $→f(a,b−1)$ +④ 合并 $a$中 $2$个 ($b$堆石子数 $+2$,堆数 $+1$) $→f(a−2,b+3)$ +⑤ 合并 $a$中 $1$个 $b$中 $1$个 ($b$堆石子数 $+1$, $a$个数 $−1$)$→f(a−1,b+1)$ -### 六、实现代码 +#### $Code$ ```cpp {.line-numbers} #include using namespace std; From fb01e15e336f830dcd06c2eb6bc01a1e0d762c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Mon, 25 Dec 2023 15:55:07 +0800 Subject: [PATCH 08/87] 'commit' --- .../T5/GameTheory/{1321_1.cpp => 1321.cpp} | 0 TangDou/AcWing_TiGao/T5/GameTheory/1321_2.cpp | 45 ------------------- 2 files changed, 45 deletions(-) rename TangDou/AcWing_TiGao/T5/GameTheory/{1321_1.cpp => 1321.cpp} (100%) delete mode 100644 TangDou/AcWing_TiGao/T5/GameTheory/1321_2.cpp diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321_1.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp similarity index 100% rename from TangDou/AcWing_TiGao/T5/GameTheory/1321_1.cpp rename to TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321_2.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1321_2.cpp deleted file mode 100644 index e9e0c1c..0000000 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1321_2.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include -using namespace std; - -int nums[60]; - -void process(int n) { - int a = 0, b = -1; - for (int i = 0; i < n; i++) { - if (nums[i] == 1) - a++; - else - b += nums[i] + 1; - } - if (a == 0 && b > 0) { - if (b % 2) - puts("YES"); - else - puts("NO"); - } - if (a > 0 && b > 2) { - if (a % 2 || b % 2) - puts("YES"); - else - puts("NO"); - } - if (a > 0 && b <= 2) { - if (a % 3) - puts("YES"); - else - puts("NO"); - } -} - -int main() { - int t, n; - cin >> t; - while (t--) { - memset(nums, 0, sizeof(nums)); - cin >> n; - for (int i = 0; i < n; i++) - cin >> nums[i]; - process(n); - } - return 0; -} \ No newline at end of file From 9e69186301b53dacffa9057f5077967d1bd9a3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Mon, 25 Dec 2023 16:17:15 +0800 Subject: [PATCH 09/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp index 84ce99a..adb76c0 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp @@ -10,7 +10,7 @@ int dfs(int a, int b) { if (!a) return v = b % 2; // 奇数为先手必胜,偶数为先手必败 // 一般5个情况 + 1个特殊情况 - + // 特殊情况: 如果操作后出现b中只有一堆,且堆中石子个数为1 // 那么应该归入到a中,并且b为0 // 以下所有情况,如果能进入必败态,先手则必胜! @@ -41,14 +41,13 @@ int main() { while (T--) { cin >> n; int a = 0, b = 0; - for (int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { // n堆石子 int x; cin >> x; - if (x == 1) a++; - // b != 0时 加1堆 + 加x石子 = 原来的 + x + 1 (其实就是区别一开始的时候) - // 当b != 0时, 我们往后加的delta - // b == 0时 加1堆 + 加x石子 = 0 + 1 + x - 1 = x - // 注意操作符优先级 + if (x == 1) a++; // 每堆石子的数量 + // b==0时 加1堆+加x石子=0 + 1+x-1=x + // b!=0时 加1堆+加x石子=原来的+x+1 + // 偏移量是1个,在b=0时,需要考虑引入这个偏移量:-1,在b>0时,就不必再次考虑了 else b += b ? x + 1 : x; } From bb796bcd904801ab11f86abaf21dd6c4487bef31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Mon, 25 Dec 2023 16:58:28 +0800 Subject: [PATCH 10/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1321.md | 195 +++++++++++++++++---- 1 file changed, 164 insertions(+), 31 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md index a72bd71..43dc7e6 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md @@ -51,52 +51,117 @@ NO 必败态 $\Rightarrow$ 选择任何路线 $\Rightarrow$ 必胜态 -### 三、本题题解 +### 三、简单情况 +为什么会想到讨论简单情况呢?我们来思考一下:如果某一堆石子只有$1$个,随着我们执行拿走$1$个的操作,它的堆就没了,这样石子个数变了,堆数也变了,两个变量,问题变复杂了,我们上来就想难题,怕是搞不定。 -先来考虑 **简单情况**:所有堆的石子个数 $>1$ +既然这样,我们就思考一下 **子空间** :只考虑所有个数大于等于$2$的那些堆,其它可能存在石子数等于$1$的,等我们想明白这个简单问题再研究扩展的事,由易到难。 -设 $b$ = 堆数 + 石子总数 $− 1$ +同时,我们需要思考博弈的胜负与什么因素相关呢?因为只有两种操作:**拿走一个石子、合并两堆**,很显然,**两个关键因素:石子个数、堆数** -可以推出: **先手必胜 $\Leftrightarrow$ $b$是奇数** +同时,两个操作同一时间只能执行一个,所以可以理解为拿走一个石子对结果影响一下,合并两堆石子对结果也是影响一下,初步考虑应该堆个数与石子总数的加法关系相关。 -**证明**:考虑以下结论: -$α$. 任意一个是奇数的情况 **可以** 被转换成一个偶数的情况 (一定存在一个偶数后继) -$β$. 任意一个是偶数的情况 **一定** 会被转换成一个奇数的情况 (所有后继都是奇数)。 +**子空间:当每堆的石子个数都是大于等于$2$时** +
设$b$ = 堆数 + 石子总数 - $1$
+
结论:$b$是奇数⟺先手必胜,$b$是偶数⟺先手必败
-先来考虑结论 $α.$ -- 如果堆数 $>1$,可以合并其中两堆石子,这样 $b$ 就会变成偶数 -- 如果堆数 $=1$,就可以拿掉 $1$ 个石子,这样 $b$ 也会变成偶数 +**证明:** -再来考虑结论 $β$. -- 合并其中的两堆石子:显然,$b$ 会变成奇数 -- 取了一个石子: - (1) 取得堆中石子个数 $>2$:石子个数 $− 1$,$b$ 会变成奇数 - (2) 取得堆中石子个数 $=2$:石子个数变成了 $1$(继续分情况讨论): - 1) 只有一堆:$b$ 是奇数,对手必胜 - 2) 多于一堆:$b$ 是奇数,对手可以把剩下的一个石子放到其他的堆里面去,对手同样必胜 +1、边界:当我们只有一堆石子且该堆石子个数为$1$个时,$b=1$,先手必胜。 -这样,我们成功的证明除了: **先手必胜 $\Leftrightarrow $ ($b$ 是奇数)** +2、当$b$为奇数,一定可以通过某种操作将$b$变成偶数 +* 如果堆数大于$1$,合并两堆让$b$变为偶数 +* 如果堆数等于$1$,从该堆石子中取出一个就可以让$b$变为偶数 +3、当$b$为偶数,无论如何操作,$b$都必将变为奇数 +* 合并两堆,则$b$变为奇数 +* 从某一堆中取走一个石子: + * 若该堆石子个数大于$2$,则$b$变为奇数,且所有堆石子数量严格大于$1$ + * 若该堆石子个数等于$2$,取一个石子后,$b$变为奇数,该堆石子个数变为$1$个,此时就再是子空间范围内了,因为出现某堆的石子个数为$1$,而不是每一堆都大于等于$2$了!需要继续分类讨论: -接下来考虑一般情况:有些堆的石子个数可能 $=1$ -假设石子个数 $=1$ 的堆数为 $a$,$b$ 仍然表示剩余石子的 **操作数**,即 (堆数 $+$ 石子总数 $− 1$) -把所有石子分成两个区域,一区域的数量 $=1$ (对应 $a$),二区域的数量都 $>1$ (对应 $b$). +#### 特殊情况 +此时为了保证所有堆的石子个数大于$1$,**足够聪明的对手** 可以进行的操作分为两类: + ① 如果只有这一堆石子,此时 **对手必胜** + ② 如果有多堆石子,可以将这一个石子合并到其他堆中,这样每对石子个数都大于$1$ + + **$Q$:对手为什么一定要采用合并的操作,而不是从别的堆中取石子呢?** + 我来举两个简单的栗子: + + * **只有一堆石子** + 石子个数是$2$个。你拿走一个,对手直接拿走另一个,游戏结束,**对手赢了**!你也是足够聪明的,你会在这种情况下这么拿吗?不能吧~,啥时候可能遇到这个情况呢?就是你被 **逼到** 这个场景下,也就是一直处于必败态! + + * **两堆石子** + 每堆石子个数是$2$个。**我是先手**,可以有两种选择: + + (1)、从任意一堆中拿走$1$个, 现在的局面是$\{2,1\}$ + $$\large 后手选择(对手) \Rightarrow + \left\{\begin{matrix} + 从2中取一个 & \Rightarrow \{1,1\} & \Rightarrow + \large \left\{\begin{matrix} + 先手合并 \Rightarrow \{2\}& 剩下一个一个取,先手胜 \\ + 先手后手一个一个取 \Rightarrow 先手败 & + \end{matrix}\right. + \\ + 从1中取一个& \Rightarrow \{2,0\} & 剩下一个一个取,先手败\\ + 合并两堆 & \Rightarrow \{3\} & 剩下一个一个取,先手胜 \\ + \end{matrix}\right. + $$ + 指望对手出错我才有赢的机会,人家要是聪明,我就废了! + + 我是先手,我肯定不能把自己的命运交到别人手中!我选择合并两堆,这样我保准赢! + + (2)、把两堆直接合并,现在的状态$\{4\}$ + 这下进入了我的套路,你取吧,你取一个,我也取一个;你再取一个,我也再取一个,结果,没有了,**对手必败**。 + + 上面的例子可能不能描述所有场景,我现在$b$是奇数,我在必胜态,我不会让自己陷入到$b$可能是偶数的状态中去,如果我选择了 + - 合并操作减少$1$个堆 + - 拿走操作减少$1$个石子 + 都会把$b-1$这个偶数态给对方 -那么可以写出以下转移: + 我不会傻到一个操作,即可能造成堆也变化,让石子个数也变化,这样就得看对方怎么选择了,而他还那么聪明,我不能犯这样的错误。 -$f(a,b)$: - -① 从 $a$中取 $1$个 $→f(a−1,b)$ -② 从 $b$中取 $1$个 $→f(a,b−1)$ -③ 合并 $b$中的 $2$个 $→f(a,b−1)$ -④ 合并 $a$中 $2$个 ($b$堆石子数 $+2$,堆数 $+1$) $→f(a−2,b+3)$ -⑤ 合并 $a$中 $1$个 $b$中 $1$个 ($b$堆石子数 $+1$, $a$个数 $−1$)$→f(a−1,b+1)$ +### 四、本题情况 +本题中可能存在一些堆的石子个数等于$1$: +* 假设有$a$堆石子,其中每堆石子个数为$1$ +* 剩余堆的石子个数都严格大于$1$ + +根据这些数量大于$1$的堆的石子可以求出上述定义出的$b$,我们使用$f(a, b)$表示此时先手必胜还是必败,因为博弈论在本质上是可以递推的,我们可以想出起点,再想出递推关系,就可以递推得到更大数据情况下的递推值,也就是博弈论本质上是$dp$。 + +
+ +相关疑问 +$Q1:$**情况**$3$**为什么是两个表达式?** +答: +①当右侧存在时,合并左边两堆石子,则右侧多出一堆石子,并且,石子个数增加$2$,也就是$b+=3$ + +②当右侧一个都没有的时候,左边送来了一堆,两个石子,按$b$的定义,是堆数+石子个数$-1=2$,即$b+=2$ + +$Q2$:**为什么用一个奇数来描述简单情况的状态,而不是用偶数呢?** +答:因为要通过递推式进行计算,最终的边界是需要我们考虑的: + +- 如果用奇数,那么边界就是$b=1$,表示只有$1$堆,石子数量只有$1$个,此时当然必胜。 +- 如果用偶数,比如边界是$b=0$,表示目前$0$堆,$0$个石子,这都啥也没有了,还必胜态,不符合逻辑,说不清道不明。 -#### $Code$ +- 那要是不用$b=0$做边界,用$b=2$呢?表示只有$1$堆,石子数量只有$1$个,这个应该也是可以,但没有再仔细想了。 + +$Q3:$**情况**$2$**从右边取一个石子,如果此时右侧存在某一堆中石子个数是$2$,取走$1$个后,变成了$1$,不就是右侧减少了一个堆,减少了两个石子,即$b-=3$;同时,此堆石子个数变为$1$,左侧个数$a+=1$,为什么没有看到这个状态变化呢?** + +答:这是因为聪明人不会从右侧某个石子数量大于$2$的堆中取走石子! + +看一下 **讨论简单情况** 中第$3$点后面的 **特殊情况**: + - 如果右侧只有一堆,石子数量为$2$,拿走$1$个,剩$1$个,一堆一个,对方必胜,此为必败态 + + - 如果右侧大于一堆,某一堆只有$2$个石子,拿走$1$个,剩$1$个,对手足够聪明,会采用右侧两堆合并的办法,此时 石子数量减$1$,堆数减$1$,对$b$的影响是减$2$,对$b$的奇偶性没有影响,换句话说,如果你现在处在必败态,你这么整完,还是必败态 + + +### 五、时间复杂度 +这里因为$a$最大取$50$,$b$最大取$50050$,因此计算这些状态的计算量为$2.5×10^6$,虽然有最多$100$次查询,但是这些状态每个只会计算一遍,因此不会超时。 + + +### 六、实现代码 ```cpp {.line-numbers} #include using namespace std; @@ -161,4 +226,72 @@ int main() { } return 0; } -``` \ No newline at end of file +``` + +思路: 我们可以简单猜想当总操作数 +∑ +a +[ +i +] ++ +n +− +1 + 是奇数的时候,先手必胜。 但事实并非如此,有些情况并不成立 。 我们可以发现,当n是1的时候,这样结论显然成立。 +另外,我们还可以总结出当没有数量为1的石子堆时,这个结论也是成立的。 下面我们先来证明这个结论。 + +猜想: 当没有数量为1的堆,上面和是奇数的时候,先手必胜,如果是偶数,先手必败。 + +1. 先证明奇数必胜,对于先手来说,如果n>1,那么只要选两堆合并, 那么总操作数变成偶数,n=1明显只能选择减少1操作,后手还是偶数。对于后手来说,无论他是减少1,还是合并操作,留下的总操作数一定还是奇数。 + +对于某些读者来说,可能会问,如果后手把某个2变成1,先手该怎么办,其实这个很容易操作,如果堆数超过1,先手一定选择合并这个数量为1的堆,如果只有一堆了而且还是1,明显先手必胜了。 所以,先手总是有办法让 + +后手必败(操作数为偶数的局面),后手无论怎么走,都会让先手必胜(变成操作为奇数的局面),所以我们证明成立。 + +2. 在上面,我们也证明了当操作数是偶数的时候,先手是必败的。 + + + +接下来我们考虑有数量为1的堆的时候的情况,这个情况比较复杂,因为如果某个人把1减少1,那么这个堆同时也消失了,相当于操作数减少了2。 + +由于数据规模不是很大,我们采用了动态规划的思想,用记忆化搜索来实现情况。 + +f +[ +a +] +[ +b +] +表示当数量为1的堆有a个,剩下的堆的操作数是b的时候,先手是必胜还是必败, +f +[ +a +] +[ +b +] += +1 +表示必胜,否则必败。b中不会出现数量为1的堆,除非只剩下一个堆了。 + +情况1: 当a>=2的时候,合并两个数量为1的堆,这样就让b这边的操作数增加了2+(b>0),因为如果原来b大于0,相当于操作数增加了3,否则b原来是0,那么操作是只增加2. + +情况2: 和a>0 并且b>0 的时候,我们可以合并一个数量为1的堆和b中一个数量不为1的堆,那么a减少1,b增加1 + +情况3:b>=2,我们合并b里面的两个堆或者减少1,无论哪种,都是让b里面的操作数减少1。 + +情况4: 当a>0的时候,我们可以减少一个数量为1的堆,这样a就减少1,b不变。 + + + +另外有一种非常特殊的情况,就是b等于1了,刚才我们说了,b里面不会出现数量为1的堆,除非只剩下一个堆了。因为b里面只要堆的数量超过1,就一定可以用合并超过替代减少1操作,这样是等价的。 + +除非b里面只有一个堆了,那么我们就只能不断减少1了。 + +所以当b是1的时候,实际我们求的问题应该变成f[a+1][0] + + + +以上内容是我自己的一个总结,网上有些题解感觉写的不是很清楚,我自己理了一下思路,重新写了一个题解。 \ No newline at end of file From f34248727840f31618832cd4c6c25e6f57b8097d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 26 Dec 2023 08:02:15 +0800 Subject: [PATCH 11/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1321.md | 107 +++------------------ 1 file changed, 16 insertions(+), 91 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md index 43dc7e6..d812cb9 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md @@ -47,30 +47,32 @@ NO ### 二、博弈论总结 - 必胜态 $\Rightarrow$ 选择合适方案 $\Rightarrow$ 必败态 - 必败态 $\Rightarrow$ 选择任何路线 $\Rightarrow$ 必胜态 - + 必胜态 $\Rightarrow$ 选择合适方案 $\Rightarrow$ 对手必败态 + 必败态 $\Rightarrow$ 选择任何路线 $\Rightarrow$ 对手必胜态 + -### 三、简单情况 -为什么会想到讨论简单情况呢?我们来思考一下:如果某一堆石子只有$1$个,随着我们执行拿走$1$个的操作,它的堆就没了,这样石子个数变了,堆数也变了,两个变量,问题变复杂了,我们上来就想难题,怕是搞不定。 - -既然这样,我们就思考一下 **子空间** :只考虑所有个数大于等于$2$的那些堆,其它可能存在石子数等于$1$的,等我们想明白这个简单问题再研究扩展的事,由易到难。 + 对手聪明绝顶,不会犯错误,一旦他有机会获胜,他一定能找到合适的方案!所以,一定不能让他有机会,也就是总要让他总是处于必败状态,你才能获胜! + -同时,我们需要思考博弈的胜负与什么因素相关呢?因为只有两种操作:**拿走一个石子、合并两堆**,很显然,**两个关键因素:石子个数、堆数** +### 三、思考过程 -同时,两个操作同一时间只能执行一个,所以可以理解为拿走一个石子对结果影响一下,合并两堆石子对结果也是影响一下,初步考虑应该堆个数与石子总数的加法关系相关。 +**$Q1$:本题中博弈的胜负与什么因素相关呢?** +**答**:因为只有两种操作:**拿走一个石子、合并两堆**,很显然, 两个关键因素: **石子个数、堆数** +**$Q2$:一般情况是什么,特殊情况是什么呢?** +**答**:如果某一堆石子只有$1$个,随着我们执行拿走$1$个的操作,它的堆就没了,这样石子个数变了,堆数也变了,一下变两个,问题变复杂了,上来就想难题,怕是搞不定。 +既然这样,我们就思考一下 **一般情况** :只考虑所有个数大于等于$2$的那些堆,其它可能存在石子数等于$1$的,等我们想明白这个一般情况问题再研究特殊情况的事,由易到难。 +**$Q3$:猜一下关联关系?** +两个操作同一时间只能执行一个,可以理解为拿走一个石子对结果影响一下,合并两堆石子对结果也是影响一下,初步考虑应该堆个数与石子总数的加法关系相关。 -**子空间:当每堆的石子个数都是大于等于$2$时** +**一般情况:当每堆的石子个数都是大于等于$2$时,猜的关联关系**
设$b$ = 堆数 + 石子总数 - $1$
结论:$b$是奇数⟺先手必胜,$b$是偶数⟺先手必败
**证明:** - -1、边界:当我们只有一堆石子且该堆石子个数为$1$个时,$b=1$,先手必胜。 - +1、**特殊情况**:当我们只有一堆石子且该堆石子个数为$1$个时,$b=1$,先手必胜。 2、当$b$为奇数,一定可以通过某种操作将$b$变成偶数 * 如果堆数大于$1$,合并两堆让$b$变为偶数 * 如果堆数等于$1$,从该堆石子中取出一个就可以让$b$变为偶数 @@ -138,16 +140,7 @@ $Q1:$**情况**$3$**为什么是两个表达式?** ②当右侧一个都没有的时候,左边送来了一堆,两个石子,按$b$的定义,是堆数+石子个数$-1=2$,即$b+=2$ -$Q2$:**为什么用一个奇数来描述简单情况的状态,而不是用偶数呢?** -答:因为要通过递推式进行计算,最终的边界是需要我们考虑的: - -- 如果用奇数,那么边界就是$b=1$,表示只有$1$堆,石子数量只有$1$个,此时当然必胜。 - -- 如果用偶数,比如边界是$b=0$,表示目前$0$堆,$0$个石子,这都啥也没有了,还必胜态,不符合逻辑,说不清道不明。 - -- 那要是不用$b=0$做边界,用$b=2$呢?表示只有$1$堆,石子数量只有$1$个,这个应该也是可以,但没有再仔细想了。 - -$Q3:$**情况**$2$**从右边取一个石子,如果此时右侧存在某一堆中石子个数是$2$,取走$1$个后,变成了$1$,不就是右侧减少了一个堆,减少了两个石子,即$b-=3$;同时,此堆石子个数变为$1$,左侧个数$a+=1$,为什么没有看到这个状态变化呢?** +$Q2:$**情况**$2$**从右边取一个石子,如果此时右侧存在某一堆中石子个数是$2$,取走$1$个后,变成了$1$,不就是右侧减少了一个堆,减少了两个石子,即$b-=3$;同时,此堆石子个数变为$1$,左侧个数$a+=1$,为什么没有看到这个状态变化呢?** 答:这是因为聪明人不会从右侧某个石子数量大于$2$的堆中取走石子! @@ -227,71 +220,3 @@ int main() { return 0; } ``` - -思路: 我们可以简单猜想当总操作数 -∑ -a -[ -i -] -+ -n -− -1 - 是奇数的时候,先手必胜。 但事实并非如此,有些情况并不成立 。 我们可以发现,当n是1的时候,这样结论显然成立。 -另外,我们还可以总结出当没有数量为1的石子堆时,这个结论也是成立的。 下面我们先来证明这个结论。 - -猜想: 当没有数量为1的堆,上面和是奇数的时候,先手必胜,如果是偶数,先手必败。 - -1. 先证明奇数必胜,对于先手来说,如果n>1,那么只要选两堆合并, 那么总操作数变成偶数,n=1明显只能选择减少1操作,后手还是偶数。对于后手来说,无论他是减少1,还是合并操作,留下的总操作数一定还是奇数。 - -对于某些读者来说,可能会问,如果后手把某个2变成1,先手该怎么办,其实这个很容易操作,如果堆数超过1,先手一定选择合并这个数量为1的堆,如果只有一堆了而且还是1,明显先手必胜了。 所以,先手总是有办法让 - -后手必败(操作数为偶数的局面),后手无论怎么走,都会让先手必胜(变成操作为奇数的局面),所以我们证明成立。 - -2. 在上面,我们也证明了当操作数是偶数的时候,先手是必败的。 - - - -接下来我们考虑有数量为1的堆的时候的情况,这个情况比较复杂,因为如果某个人把1减少1,那么这个堆同时也消失了,相当于操作数减少了2。 - -由于数据规模不是很大,我们采用了动态规划的思想,用记忆化搜索来实现情况。 - -f -[ -a -] -[ -b -] -表示当数量为1的堆有a个,剩下的堆的操作数是b的时候,先手是必胜还是必败, -f -[ -a -] -[ -b -] -= -1 -表示必胜,否则必败。b中不会出现数量为1的堆,除非只剩下一个堆了。 - -情况1: 当a>=2的时候,合并两个数量为1的堆,这样就让b这边的操作数增加了2+(b>0),因为如果原来b大于0,相当于操作数增加了3,否则b原来是0,那么操作是只增加2. - -情况2: 和a>0 并且b>0 的时候,我们可以合并一个数量为1的堆和b中一个数量不为1的堆,那么a减少1,b增加1 - -情况3:b>=2,我们合并b里面的两个堆或者减少1,无论哪种,都是让b里面的操作数减少1。 - -情况4: 当a>0的时候,我们可以减少一个数量为1的堆,这样a就减少1,b不变。 - - - -另外有一种非常特殊的情况,就是b等于1了,刚才我们说了,b里面不会出现数量为1的堆,除非只剩下一个堆了。因为b里面只要堆的数量超过1,就一定可以用合并超过替代减少1操作,这样是等价的。 - -除非b里面只有一个堆了,那么我们就只能不断减少1了。 - -所以当b是1的时候,实际我们求的问题应该变成f[a+1][0] - - - -以上内容是我自己的一个总结,网上有些题解感觉写的不是很清楚,我自己理了一下思路,重新写了一个题解。 \ No newline at end of file From f75598f49dfbcffa85e5fc946e49e20963739dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 26 Dec 2023 08:15:23 +0800 Subject: [PATCH 12/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1321.md | 96 ++++++---------------- 1 file changed, 26 insertions(+), 70 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md index d812cb9..a8dca4e 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md @@ -71,87 +71,43 @@ NO
设$b$ = 堆数 + 石子总数 - $1$
结论:$b$是奇数⟺先手必胜,$b$是偶数⟺先手必败
-**证明:** -1、**特殊情况**:当我们只有一堆石子且该堆石子个数为$1$个时,$b=1$,先手必胜。 -2、当$b$为奇数,一定可以通过某种操作将$b$变成偶数 -* 如果堆数大于$1$,合并两堆让$b$变为偶数 -* 如果堆数等于$1$,从该堆石子中取出一个就可以让$b$变为偶数 - -3、当$b$为偶数,无论如何操作,$b$都必将变为奇数 -* 合并两堆,则$b$变为奇数 -* 从某一堆中取走一个石子: - * 若该堆石子个数大于$2$,则$b$变为奇数,且所有堆石子数量严格大于$1$ - * 若该堆石子个数等于$2$,取一个石子后,$b$变为奇数,该堆石子个数变为$1$个,此时就再是子空间范围内了,因为出现某堆的石子个数为$1$,而不是每一堆都大于等于$2$了!需要继续分类讨论: - -#### 特殊情况 -此时为了保证所有堆的石子个数大于$1$,**足够聪明的对手** 可以进行的操作分为两类: - ① 如果只有这一堆石子,此时 **对手必胜** - ② 如果有多堆石子,可以将这一个石子合并到其他堆中,这样每对石子个数都大于$1$ - - **$Q$:对手为什么一定要采用合并的操作,而不是从别的堆中取石子呢?** - 我来举两个简单的栗子: - - * **只有一堆石子** - 石子个数是$2$个。你拿走一个,对手直接拿走另一个,游戏结束,**对手赢了**!你也是足够聪明的,你会在这种情况下这么拿吗?不能吧~,啥时候可能遇到这个情况呢?就是你被 **逼到** 这个场景下,也就是一直处于必败态! - - * **两堆石子** - 每堆石子个数是$2$个。**我是先手**,可以有两种选择: - - (1)、从任意一堆中拿走$1$个, 现在的局面是$\{2,1\}$ - $$\large 后手选择(对手) \Rightarrow - \left\{\begin{matrix} - 从2中取一个 & \Rightarrow \{1,1\} & \Rightarrow - \large \left\{\begin{matrix} - 先手合并 \Rightarrow \{2\}& 剩下一个一个取,先手胜 \\ - 先手后手一个一个取 \Rightarrow 先手败 & - \end{matrix}\right. - \\ - 从1中取一个& \Rightarrow \{2,0\} & 剩下一个一个取,先手败\\ - 合并两堆 & \Rightarrow \{3\} & 剩下一个一个取,先手胜 \\ - \end{matrix}\right. - $$ - 指望对手出错我才有赢的机会,人家要是聪明,我就废了! - - 我是先手,我肯定不能把自己的命运交到别人手中!我选择合并两堆,这样我保准赢! - - (2)、把两堆直接合并,现在的状态$\{4\}$ - 这下进入了我的套路,你取吧,你取一个,我也取一个;你再取一个,我也再取一个,结果,没有了,**对手必败**。 - - 上面的例子可能不能描述所有场景,我现在$b$是奇数,我在必胜态,我不会让自己陷入到$b$可能是偶数的状态中去,如果我选择了 - - 合并操作减少$1$个堆 - - 拿走操作减少$1$个石子 - 都会把$b-1$这个偶数态给对方 +我们可以发现,当$n$是$1$的时候,这样结论显然成立。 - 我不会傻到一个操作,即可能造成堆也变化,让石子个数也变化,这样就得看对方怎么选择了,而他还那么聪明,我不能犯这样的错误。 +然后分类讨论: -### 四、本题情况 -本题中可能存在一些堆的石子个数等于$1$: -* 假设有$a$堆石子,其中每堆石子个数为$1$ -* 剩余堆的石子个数都严格大于$1$ +**情况$1$:没有数量为$1$的堆** + +先证明奇数必胜,对于先手来说, + - $n>1$, 那么 **只要选两堆合并**, 那么 **总操作数变成偶数** + - $n=1$, 明显只能选择减少$1$操作,后手还是偶数。 + +对于后手来说,无论他是减少$1$,还是合并操作,留下的总操作数一定还是奇数。 + +对于某些读者来说,可能会问,如果后手把某个$2$变成$1$,先手该怎么办,其实这个很容易操作,如果堆数超过$1$,先手一定选择合并这个数量为$1$的堆,如果只有一堆了而且还是$1$,明显先手必胜了。 所以,先手总是有办法让后手必败(操作数为偶数的局面),后手无论怎么走,都会让先手必胜(变成操作为奇数的局面),所以我们证明成立。 + +在上面,我们也证明了当操作数是偶数的时候,先手是必败的。 + -根据这些数量大于$1$的堆的石子可以求出上述定义出的$b$,我们使用$f(a, b)$表示此时先手必胜还是必败,因为博弈论在本质上是可以递推的,我们可以想出起点,再想出递推关系,就可以递推得到更大数据情况下的递推值,也就是博弈论本质上是$dp$。 +**情况$2$:有数量为$1$的堆** -
+这个情况比较复杂,因为如果某个人把$1$减少$1$,那么这个堆同时也消失了,相当于操作数减少了$2$。 -相关疑问 -$Q1:$**情况**$3$**为什么是两个表达式?** -答: -①当右侧存在时,合并左边两堆石子,则右侧多出一堆石子,并且,石子个数增加$2$,也就是$b+=3$ +下面我们采用了动态规划的思想,用记忆化搜索来模拟演示一下: -②当右侧一个都没有的时候,左边送来了一堆,两个石子,按$b$的定义,是堆数+石子个数$-1=2$,即$b+=2$ +$f[a][b]$ 表示当数量为$1$的堆有$a$个,剩下的堆的操作数是$b$的时候,先手是必胜还是必败, +f[a][b]=1 表示必胜,否则必败。$b$中不会出现数量为$1$的堆,除非只剩下一个堆了。 -$Q2:$**情况**$2$**从右边取一个石子,如果此时右侧存在某一堆中石子个数是$2$,取走$1$个后,变成了$1$,不就是右侧减少了一个堆,减少了两个石子,即$b-=3$;同时,此堆石子个数变为$1$,左侧个数$a+=1$,为什么没有看到这个状态变化呢?** +情况1: 当a>=2的时候,合并两个数量为1的堆,这样就让b这边的操作数增加了2+(b>0),因为如果原来b大于0,相当于操作数增加了3,否则b原来是0,那么操作是只增加2. +情况2: 和a>0 并且b>0 的时候,我们可以合并一个数量为1的堆和b中一个数量不为1的堆,那么a减少1,b增加1 +情况3:b>=2,我们合并b里面的两个堆或者减少1,无论哪种,都是让b里面的操作数减少1。 +情况4: 当a>0的时候,我们可以减少一个数量为1的堆,这样a就减少1,b不变。 -答:这是因为聪明人不会从右侧某个石子数量大于$2$的堆中取走石子! -看一下 **讨论简单情况** 中第$3$点后面的 **特殊情况**: - - 如果右侧只有一堆,石子数量为$2$,拿走$1$个,剩$1$个,一堆一个,对方必胜,此为必败态 - - - 如果右侧大于一堆,某一堆只有$2$个石子,拿走$1$个,剩$1$个,对手足够聪明,会采用右侧两堆合并的办法,此时 石子数量减$1$,堆数减$1$,对$b$的影响是减$2$,对$b$的奇偶性没有影响,换句话说,如果你现在处在必败态,你这么整完,还是必败态 +另外有一种非常特殊的情况,就是$b$等于$1$了,刚才我们说了,$b$里面不会出现数量为$1$的堆,除非只剩下一个堆了。因为$b$里面只要堆的数量超过$1$,就一定可以用合并超过替代减少$1$操作,这样是等价的。 +除非$b$里面只有一个堆了,那么我们就只能不断减少$1$了。 -### 五、时间复杂度 -这里因为$a$最大取$50$,$b$最大取$50050$,因此计算这些状态的计算量为$2.5×10^6$,虽然有最多$100$次查询,但是这些状态每个只会计算一遍,因此不会超时。 +所以当$b$是$1$的时候,实际我们求的问题应该变成$f[a+1][0]$ ### 六、实现代码 From 8f454b820c2cd655fc7a691485513ee158304807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 26 Dec 2023 08:33:19 +0800 Subject: [PATCH 13/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1321.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md index a8dca4e..759f309 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md @@ -92,22 +92,20 @@ NO 这个情况比较复杂,因为如果某个人把$1$减少$1$,那么这个堆同时也消失了,相当于操作数减少了$2$。 -下面我们采用了动态规划的思想,用记忆化搜索来模拟演示一下: -$f[a][b]$ 表示当数量为$1$的堆有$a$个,剩下的堆的操作数是$b$的时候,先手是必胜还是必败, -f[a][b]=1 表示必胜,否则必败。$b$中不会出现数量为$1$的堆,除非只剩下一个堆了。 +本题中可能存在一些堆的石子个数等于$1$: +* 假设有$a$堆石子,其中每堆石子个数为$1$ +* 剩余堆的石子个数都严格大于$1$ -情况1: 当a>=2的时候,合并两个数量为1的堆,这样就让b这边的操作数增加了2+(b>0),因为如果原来b大于0,相当于操作数增加了3,否则b原来是0,那么操作是只增加2. -情况2: 和a>0 并且b>0 的时候,我们可以合并一个数量为1的堆和b中一个数量不为1的堆,那么a减少1,b增加1 -情况3:b>=2,我们合并b里面的两个堆或者减少1,无论哪种,都是让b里面的操作数减少1。 -情况4: 当a>0的时候,我们可以减少一个数量为1的堆,这样a就减少1,b不变。 +根据这些数量大于$1$的堆的石子可以求出上述定义出的$b$,我们使用$f(a, b)$表示此时先手必胜还是必败,因为博弈论在本质上是可以递推的,我们可以想出起点,再想出递推关系,就可以递推得到更大数据情况下的递推值,也就是博弈论本质上是$dp$。 +
-另外有一种非常特殊的情况,就是$b$等于$1$了,刚才我们说了,$b$里面不会出现数量为$1$的堆,除非只剩下一个堆了。因为$b$里面只要堆的数量超过$1$,就一定可以用合并超过替代减少$1$操作,这样是等价的。 +$Q:$**情况**$3$**为什么是两个表达式?** +答: +①当右侧存在时,合并左边两堆石子,则右侧多出一堆石子,并且,石子个数增加$2$,也就是$b+=3$ -除非$b$里面只有一个堆了,那么我们就只能不断减少$1$了。 - -所以当$b$是$1$的时候,实际我们求的问题应该变成$f[a+1][0]$ +②当右侧一个都没有的时候,左边送来了一堆,两个石子,按$b$的定义,是堆数+石子个数$-1=2$,即$b+=2$ ### 六、实现代码 From 2c276652a10a633b9341468cd6aae6fe1c0d04be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 26 Dec 2023 10:53:00 +0800 Subject: [PATCH 14/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp | 75 +++++++------------ TangDou/AcWing_TiGao/T5/GameTheory/1321.eddx | Bin 0 -> 100485 bytes TangDou/AcWing_TiGao/T5/GameTheory/1321.md | 17 +++-- 3 files changed, 38 insertions(+), 54 deletions(-) create mode 100644 TangDou/AcWing_TiGao/T5/GameTheory/1321.eddx diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp index adb76c0..67a2816 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp @@ -1,59 +1,38 @@ -#include +#include +#include using namespace std; -const int N = 55, M = 50050; // M 包括了 50 * 1000 + 50个石子数量为1的堆数 -int f[N][M]; - -int dfs(int a, int b) { - int &v = f[a][b]; - if (~v) return v; - // 简单情况: 即所有堆的石子个数都是严格大于1,此时a是0 - if (!a) return v = b % 2; // 奇数为先手必胜,偶数为先手必败 - - // 一般5个情况 + 1个特殊情况 - - // 特殊情况: 如果操作后出现b中只有一堆,且堆中石子个数为1 - // 那么应该归入到a中,并且b为0 - // 以下所有情况,如果能进入必败态,先手则必胜! - if (b == 1) return dfs(a + 1, 0); - - // 情况1:有a,从a中取一个 - if (a && !dfs(a - 1, b)) return v = 1; - - // 情况2, 4:有b,从b中取1个(石子总数 - 1) or 合并b中两堆(堆数 - 1), - if (b && !dfs(a, b - 1)) return v = 1; - - // 情况3:有a >= 2, 合并a中两个 - // 如果b的堆数不为0, a - 2, b + 1堆 + 2个石子(只需要加delta) ====> b + 3 - // 如果b的堆数为0, a - 2, 0 + 2个石子 + 1堆 - 1 ====> b + 2 - if (a >= 2 && !dfs(a - 2, b + (b ? 3 : 2))) return v = 1; - - // 情况5:有a,有b, 合并a中1个b中1个, a - 1, b的堆数无变化 + 1个石子(只加delta) - if (a && b && !dfs(a - 1, b + 1)) return v = 1; - - // 其他情况,则先手处于必败状态 - return v = 0; +const int N = 55, M = 50060; +int n, f[N][M]; +int sg(int a, int b) { + if (f[a][b] != -1) return f[a][b]; + int &s = f[a][b]; + if (!a) return b & 1; // 如果能转移到必败态 + if (b == 1) return sg(a + 1, b - 1); + if (a && !sg(a - 1, b)) return s = 1; // 取a + if (b && !sg(a, b - 1)) return s = 1; // 合并b,取b + if (a && b > 1 && !sg(a - 1, b + 1)) return s = 1; // 合并a,b + if (a > 1 && !sg(a - 2, b == 0 ? b + 2 : b + 3)) return s = 1; // 合并a + return s = 0; } - int main() { + int T; + scanf("%d", &T); memset(f, -1, sizeof f); - int T, n; - cin >> T; + f[1][0] = f[2][0] = 1; + f[3][0] = 0; while (T--) { - cin >> n; - int a = 0, b = 0; - for (int i = 0; i < n; i++) { // n堆石子 + scanf("%d", &n); + int a = 0, b = -1; + for (int i = 1; i <= n; i++) { int x; - cin >> x; - if (x == 1) a++; // 每堆石子的数量 - // b==0时 加1堆+加x石子=0 + 1+x-1=x - // b!=0时 加1堆+加x石子=原来的+x+1 - // 偏移量是1个,在b=0时,需要考虑引入这个偏移量:-1,在b>0时,就不必再次考虑了 + scanf("%d", &x); + if (x == 1) + a++; else - b += b ? x + 1 : x; + b += x + 1; } - - // 1 为先手必胜, 0为先手必败 - if (dfs(a, b)) + if (b < 0) b = 0; + if (sg(a, b)) puts("YES"); else puts("NO"); diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321.eddx b/TangDou/AcWing_TiGao/T5/GameTheory/1321.eddx new file mode 100644 index 0000000000000000000000000000000000000000..e5f68389b6a60f37d745a09ec53070320a1e15e1 GIT binary patch literal 100485 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%0DJ&xxh^lEjdLj!b#Huh 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 a`4=4PG{Z?x zhe2*-9+s_jJWAn&sJ~6zGF46`Ijlq+=gW3Z0|t06Jmt5M+^{pCYHLx#I*DHTk4!0psf&~=A;OsJ@da~2Se_wv3M)YY{Hog{&Rrnp- zc+Vhti+V|tq^t^3=PcEYR!S!5fUTAVlB??tG16OvNrU!1AgMPGS=gFRW~0-tn`Kx9 zDQPzn&Mcq|ol7yYxJZOH*EfH49cfbaUD1Kul#G4``z1yUjN`!LIIz@7c?m8ewsB9g zf0c^YV>Bwrm~r@{va5e3WvN&>Y}L#uHfCxftE4_IUUd(dbMiZ( zZ3%k?$2Y?SZm3S89IxU~uMg6sRgO9r-#~Gq4i6s==+LYK^;inBxFrrNqgU*ii7?l^ zft{joOpn=5O=McLM4kIB1w%l!)x2uMZd7xcUYhd{$JY~hp<1vUKCZhDY05a`z03=- z5n9NHCA5jMsZj6>yD71XFA31o_!cbpyVZCf!mcSl63Bjz8?bK&1QfRWdA?K)3Mwml z_$QpP&1?@wGBP;0u z^VdcYeTb`)pfuXJ4>^Ul?*~X0`rC&00vk}!2T8uSDvH(Gas}>b&MV69r&kdGwJRg# z`Fr-?c%9vF{^V@6dVqIrW7%)Q}v>Qubc=D`lj{3Iwg=*te2}+ z^X>B9EMP6G(R~e{+4o_!`y~Gbh-M+;O@zR57FEcSwK3Do))c6;0Yo;MAl@PJ6`9wK z5H^El`N^lS+X)EJm-SB=<@~2H!y(_)G zaBAAckCBIF&(pX6d?8Bz@TY(io7;EwpZ_hYf8EUX^vk8+3pc&r`_mS-To7SC>7kZ|g3tzy9|BickJ4{{^r8)Bbq-lmA`&_p|Egw*HY{`9D2if1Rz0 z$EV5vkN>&*$I8pYtC+nf~kWC&{nzkG`j`|Nl7izTyA#e}3F>V6R;I zpGV@+LjAR?;-Ze8jayZfn|OB3mY*+APp^xP&fHcJI_KRL+tlUW;c1aK?2 z>&weGohg0&#Ag1gdvAN=uN+uvXteR~|JlGZX&9MAm_>jGv~VzVgv|=id--zLN#Kbz zYAg&4Jm7;{fKouPtr0|~i5Fhp3=DsPSOCR{ z&p?B7Q&Tb%^)qu5(^C!g3i8r%8Z(vY>Vcyx85k^AqZ^a$i(-rsUSn8_mrvdA&cIN( znT3HL#Wiu?85lt4?G)>SPu;=mC)6W>K^~4`!LpHz+K|TKm zr%9-X>mZxd@4$#SY6qu&)T2C*^(VT3^dleafn*3P8_3rjK&Z{cz~Isbj7|msrTC)+ literal 0 HcmV?d00001 diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md index 759f309..e47be1e 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md @@ -66,14 +66,20 @@ NO **$Q3$:猜一下关联关系?** 两个操作同一时间只能执行一个,可以理解为拿走一个石子对结果影响一下,合并两堆石子对结果也是影响一下,初步考虑应该堆个数与石子总数的加法关系相关。 -**一般情况:当每堆的石子个数都是大于等于$2$时,猜的关联关系** +**一般情况:当每堆的石子个数都是大于等于$2$时,猜关联关系** -

设$b$ = 堆数 + 石子总数 - $1$
+
设 剩余操作数 = $b$ = 堆数 + 石子总数 - $1$
结论:$b$是奇数⟺先手必胜,$b$是偶数⟺先手必败
-我们可以发现,当$n$是$1$的时候,这样结论显然成立。 +我们可以发现,当$n$是$1$的时候,也就是只有$1$堆时,比如$a_0=3$,那么$b=3+1-1=3$,是奇数: +- ① 先手拿走一个,剩操作数=$2$ +- ② 后手只能拿走$1$个,剩操作数$1$ +- ③ 先手再拿走1个,剩余操作数$=0$ +- ④ 后手没有可以拿的了,后手负,先手必胜! -然后分类讨论: +结论显然成立。 + +当不只有$1$堆时,分类讨论: **情况$1$:没有数量为$1$的堆** @@ -92,8 +98,6 @@ NO 这个情况比较复杂,因为如果某个人把$1$减少$1$,那么这个堆同时也消失了,相当于操作数减少了$2$。 - -本题中可能存在一些堆的石子个数等于$1$: * 假设有$a$堆石子,其中每堆石子个数为$1$ * 剩余堆的石子个数都严格大于$1$ @@ -108,6 +112,7 @@ $Q:$**情况**$3$**为什么是两个表达式?** ②当右侧一个都没有的时候,左边送来了一堆,两个石子,按$b$的定义,是堆数+石子个数$-1=2$,即$b+=2$ + ### 六、实现代码 ```cpp {.line-numbers} #include From 1155284ae88ce39b70d94917f0ee5acd1228cadb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 26 Dec 2023 11:03:30 +0800 Subject: [PATCH 15/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp | 45 ++++++++++++--------- TangDou/AcWing_TiGao/T5/GameTheory/1321.md | 6 +-- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp index 67a2816..7f6502d 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1321.cpp @@ -1,38 +1,43 @@ -#include -#include +#include using namespace std; const int N = 55, M = 50060; int n, f[N][M]; -int sg(int a, int b) { - if (f[a][b] != -1) return f[a][b]; - int &s = f[a][b]; - if (!a) return b & 1; // 如果能转移到必败态 - if (b == 1) return sg(a + 1, b - 1); - if (a && !sg(a - 1, b)) return s = 1; // 取a - if (b && !sg(a, b - 1)) return s = 1; // 合并b,取b - if (a && b > 1 && !sg(a - 1, b + 1)) return s = 1; // 合并a,b - if (a > 1 && !sg(a - 2, b == 0 ? b + 2 : b + 3)) return s = 1; // 合并a - return s = 0; + +int dfs(int a, int b) { + if (~f[a][b]) return f[a][b]; // 记忆化搜索 + int &v = f[a][b]; // 引用,赋值更快捷 + if (!a) return b & 1; // a==0时,看b是不是奇数,奇数必胜,否则必败 + + if (b == 1) return dfs(a + 1, b - 1); + if (a && !dfs(a - 1, b)) return v = 1; // 从左边取一石子:左侧不空,并且取完后整体是一个必败态,那我必胜 + if (b && !dfs(a, b - 1)) return v = 1; // 合并b + if (a && b > 1 && !dfs(a - 1, b + 1)) return v = 1; // 合并a,b各一个 + if (a > 1 && !dfs(a - 2, b == 0 ? b + 2 : b + 3)) return v = 1; // 合并a + return v = 0; } int main() { int T; - scanf("%d", &T); - memset(f, -1, sizeof f); + cin >> T; + memset(f, -1, sizeof f); // 初始化DP数组,-1 + + // 边界初始化 f[1][0] = f[2][0] = 1; f[3][0] = 0; + while (T--) { - scanf("%d", &n); + cin >> n; int a = 0, b = -1; for (int i = 1; i <= n; i++) { int x; - scanf("%d", &x); + cin >> x; if (x == 1) - a++; + a++; // 左侧石子个数为1的石子堆数量 else - b += x + 1; + b += x + 1; // 1:新增加1堆,x:这一堆x个 } - if (b < 0) b = 0; - if (sg(a, b)) + if (b < 0) b = 0; // 一堆都没有,b=0 + + if (dfs(a, b)) puts("YES"); else puts("NO"); diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md index e47be1e..e6d5553 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md @@ -105,12 +105,8 @@ NO
-$Q:$**情况**$3$**为什么是两个表达式?** -答: -①当右侧存在时,合并左边两堆石子,则右侧多出一堆石子,并且,石子个数增加$2$,也就是$b+=3$ - -②当右侧一个都没有的时候,左边送来了一堆,两个石子,按$b$的定义,是堆数+石子个数$-1=2$,即$b+=2$ +![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202312261053532.png) ### 六、实现代码 From 8b7f69eb91b7cfa1196d828784c560f248d57178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 26 Dec 2023 14:37:06 +0800 Subject: [PATCH 16/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1321.md | 65 ++- TangDou/AcWing_TiGao/T5/GameTheory/1322.md | 74 +--- TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md | 401 ++++++++++++++++++ TangDou/AcWing_TiGao/T5/GameTheory/test.cpp | 27 -- .../AcWing_TiGao/T5/GameTheory/博弈论.md | 115 ----- 5 files changed, 437 insertions(+), 245 deletions(-) create mode 100644 TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md delete mode 100644 TangDou/AcWing_TiGao/T5/GameTheory/test.cpp diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md index e6d5553..add2c6b 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md @@ -113,60 +113,43 @@ NO ```cpp {.line-numbers} #include using namespace std; -const int N = 55, M = 50050; // M 包括了 50 * 1000 + 50个石子数量为1的堆数 -int f[N][M]; +const int N = 55, M = 50060; +int n, f[N][M]; int dfs(int a, int b) { - int &v = f[a][b]; - if (~v) return v; - // 简单情况: 即所有堆的石子个数都是严格大于1,此时a是0 - if (!a) return v = b % 2; // 奇数为先手必胜,偶数为先手必败 - - // 一般5个情况 + 1个特殊情况 - - // 特殊情况: 如果操作后出现b中只有一堆,且堆中石子个数为1 - // 那么应该归入到a中,并且b为0 - // 以下所有情况,如果能进入必败态,先手则必胜! - if (b == 1) return dfs(a + 1, 0); - - // 情况1:有a,从a中取一个 - if (a && !dfs(a - 1, b)) return v = 1; - - // 情况2, 4:有b,从b中取1个(石子总数 - 1) or 合并b中两堆(堆数 - 1), - if (b && !dfs(a, b - 1)) return v = 1; - - // 情况3:有a >= 2, 合并a中两个 - // 如果b的堆数不为0, a - 2, b + 1堆 + 2个石子(只需要加delta) ====> b + 3 - // 如果b的堆数为0, a - 2, 0 + 2个石子 + 1堆 - 1 ====> b + 2 - if (a >= 2 && !dfs(a - 2, b + (b ? 3 : 2))) return v = 1; - - // 情况5:有a,有b, 合并a中1个b中1个, a - 1, b的堆数无变化 + 1个石子(只加delta) - if (a && b && !dfs(a - 1, b + 1)) return v = 1; - - // 其他情况,则先手处于必败状态 + if (~f[a][b]) return f[a][b]; // 记忆化搜索 + int &v = f[a][b]; // 引用,赋值更快捷 + if (!a) return b & 1; // a==0时,看b是不是奇数,奇数必胜,否则必败 + + if (b == 1) return dfs(a + 1, b - 1); + if (a && !dfs(a - 1, b)) return v = 1; // 从左边取一石子:左侧不空,并且取完后整体是一个必败态,那我必胜 + if (b && !dfs(a, b - 1)) return v = 1; // 合并b + if (a && b > 1 && !dfs(a - 1, b + 1)) return v = 1; // 合并a,b各一个 + if (a > 1 && !dfs(a - 2, b == 0 ? b + 2 : b + 3)) return v = 1; // 合并a return v = 0; } - int main() { - memset(f, -1, sizeof f); - int T, n; + int T; cin >> T; + memset(f, -1, sizeof f); // 初始化DP数组,-1 + + // 边界初始化 + f[1][0] = f[2][0] = 1; + f[3][0] = 0; + while (T--) { cin >> n; - int a = 0, b = 0; - for (int i = 0; i < n; i++) { + int a = 0, b = -1; + for (int i = 1; i <= n; i++) { int x; cin >> x; - if (x == 1) a++; - // b != 0时 加1堆 + 加x石子 = 原来的 + x + 1 (其实就是区别一开始的时候) - // 当b != 0时, 我们往后加的delta - // b == 0时 加1堆 + 加x石子 = 0 + 1 + x - 1 = x - // 注意操作符优先级 + if (x == 1) + a++; // 左侧石子个数为1的石子堆数量 else - b += b ? x + 1 : x; + b += x + 1; // 1:新增加1堆,x:这一堆x个 } + if (b < 0) b = 0; // 一堆都没有,b=0 - // 1 为先手必胜, 0为先手必败 if (dfs(a, b)) puts("YES"); else diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md index cfd80b7..f252ff4 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md @@ -1,15 +1,3 @@ - - - - ##[$AcWing 1322$. 取石子游戏](https://www.acwing.com/problem/content/1324/) ### 一、题目描述 @@ -45,62 +33,26 @@ $1≤T≤10,1≤n≤1000,1≤a_i≤10^9$ 0 ``` -### 二、状态定义 - -**① 原来每堆数量是长成这个样的,可能是必胜状态,也可能是必败状态,都可以:** - -
- -| $a_i$ | $a_{i+1}$ | ... | $a_{j-1}$ | $a_{j}$ | -| ---- | ---- |---- | ---- | ---- | ---- | ---- | - -
- - -- 设 $left[i][j]$ 表示在 必胜区间 $[i,j]$ 区间的 **左侧** 放上一堆数量为 $left[i][j]$ 的石子后,**先手必败** -- 设 $right[i][j]$ 表示在 必胜区间 $[i,j]$ 区间的 **右侧** 放上一堆数量为 $right[i][j]$ 的石子后,**先手必败** - -**② 假如原来$a_i \sim a_j$为必胜态,那么你前面添上啥都是必败的** -**③ 假如原来$a_i \sim a_j$为必败态,那么你前面添上$left[i][j]=0$ 也还是必败的** - -**总结**:不管原来$a_i \sim a_j$是啥状态,反正,都可以通过向左边添加一个堆的方法(堆的厂子数量可以为$0$)使得状态改为 **先手必败** - -
- -| $left[i][j]$ | $a_i$ | $a_{i+1}$ | ... | $a_{j-1}$ | $a_{j}$ | $right[i][j]$ | -| ---- | ---- |---- | ---- | ---- | ---- | ---- | +### 二、思考过程 -
+#### 1、状态定义 - +① 设 $left[i][j]$ 表示在 $[i,j]$ 已经固定的区间 **左侧** 放上一堆数量为 $left[i][j]$ 的石子后,**先手必败** +② 设 $right[i][j]$ 表示在 $[i,j]$ 已经固定的区间 **右侧** 放上一堆数量为 $right[i][j]$ 的石子后,**先手必败** 即:$(left[i][j],\underbrace{a_i,a_{i+1},\cdots,a_j}_{a[i]\sim a[j]})$,$(\underbrace{a_i,a_{i+1},\cdots,a_j}_{a[i]\sim a[j]},right[i][j])$ 为 **先手必败** 局面 -### 三、$left[i][j]$ 的**存在性证明** -博弈论的题,时刻要记得 +#### 2、神奇的性质 +① $left[i][j]$,$right[i][j]$一定存在 +② $left[i][j]$,$right[i][j]$必然唯一 -**转化关系** +##### (1) $left[i][j]$ 的唯一性证明 -$$ -必胜态 \rightarrow -\large \left\{\begin{matrix} - 合适的办法 & \rightarrow & 必败态(让对手必败) \\ - 走错了(傻了) & \rightarrow & 必胜态(让对手必胜) -\end{matrix}\right. -$$ - -$$ -必败态 \rightarrow -\large \left\{\begin{matrix} - 无论怎么走(绝望) & \rightarrow & 必胜态(让对手必胜) \\ - 永远无法(绝望) & \rightarrow & 必败态(让对手必败) -\end{matrix}\right. -$$ - - -($right[i][j]$ 同理,下同): +**反证法**: +假设 $left(i,j)$ 不唯一,则存在非负整数 $x_1,x_2(x_1 \neq x_2)$,使得$(x_1,a_i,a_{i+1},⋯,a_{j−1},a_j)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 均为必败局面,而 **第一个必败局面** 可以通过拿走左侧$x_1-x_2$个石子到达另一个 **必败局面** ,矛盾,假设不成立,唯一性显然。 +##### (2) $left[i][j]$ 的**存在性证明** 反证法: 假设不存在满足定义的 $left[i][j]$,则对于 **任意非负整数** $x$,有形如: @@ -117,9 +69,7 @@ $$\large \underbrace{x,a_i,a_{i+1},\cdots,a_j}_{A(x)}$$ 都为**必胜局面**, **由于 $x$ 有无限个,但 $y$ 只有 $a_j$种——根据抽屉原理,必存在 $x_1,x_2(x_1 \neq x_2),y$ 满足 $(x_1,a_i,a_{i+1},\cdots,a_{j-1},y)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},y)$ 都是必败局面**。但这两个必败局面之间 **实际一步可达**,故矛盾,进而原命题成立。 -### 四、$left[i][j]$ 的唯一性证明 -反证法: -假设 $left(i,j)$ 不唯一,则存在非负整数 $x_1,x_2(x_1 \neq x_2)$,使得$(x_1,a_i,a_{i+1},⋯,a_{j−1},a_j)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 均为必败局面,而这两个必败局面之间 **实际一步可达** ,故矛盾,进而原命题成立。 + ### 五、状态转移 diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md new file mode 100644 index 0000000..bb2fb98 --- /dev/null +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md @@ -0,0 +1,401 @@ +##[$AcWing 1322$. 取石子游戏](https://www.acwing.com/problem/content/1324/) + +### 一、题目描述 +在研究过 $Nim$ 游戏及各种变种之后,$Orez$ 又发现了一种全新的取石子游戏,这个游戏是这样的: + +有 $n$ 堆石子,将这 $n$ 堆石子摆成一排。 + +游戏由两个人进行,两人轮流操作,每次操作者都可以从 **最左** 或 **最右** 的一堆中取出若干颗石子,可以将那一堆全部取掉,但不能不取,**不能操作的人就输了**。 + +$Orez$ 问:对于任意给出的一个初始局面,是否存在先手必胜策略。 + +**输入格式** +第一行为一个整数 $T$,表示有 $T$ 组测试数据。 + +对于每组测试数据,第一行为一个整数 $n$,表示有 $n$ 堆石子,第二行为 $n$ 个整数 $a_i$ ,依次表示每堆石子的数目。 + +**输出格式** +对于每组测试数据仅输出一个整数 $0$ 或 $1$,占一行。 + +其中 $1$ 表示有先手必胜策略,$0$ 表示没有。 + +**数据范围** +$1≤T≤10,1≤n≤1000,1≤a_i≤10^9$ + +**输入样例**: +```cpp {.line-numbers} +1 +4 +3 1 9 4 +``` +输出样例: +```cpp {.line-numbers} +0 +``` + +### 解题思路 +首先定义两个状态: +① $left[i][j]$ : 我在$[i,j]$这个区间的左边放上一堆数量是多少的石头能够让我先手必败. +② $right[i][j]$: 我在$[i,j]$区间的左边放上一堆数量是多少的石头能让我先手必败. + +$left[i][j]$ 的性质:**必然存在且唯一**. + +**证明**:假设在区间左边放上$L_1,L_2$ 数量的石堆都能使我必败,不妨设$L_1R +� +> +� +且RR +� +> +� +,那么后手可以在另一边取先手取得数量少一的数量,这样对于每次先手取完之后剩余X>R +� +> +� +的情况来说,后手都可以在另一边取. +取完之后X=R +� += +� +,那么就对应情况1,后手把另一边的石堆取光 +取完之后X=R +� +>= +� +,那么后手在另一边取数量加一的数量,对于每次先手取的情况,后手都可以在另一边取. +取完之后XL +� +> +� +且L<=X=L +� +>= +� +,后手就在另一边取数量加一的石头 +取完之后X=L+1 +� +>= +� ++ +1 +,后手就在另一边取数量减一的石头 +取完之后X<=L +� +<= +� +,后手就把右边的石头取完 +取完之后XL +� +> +� +且X>R +� +> +� +, left[i][j]=X +� +� +� +� +[ +� +] +[ +� +] += +� +4.1: +X>L>R +� +> +� +> +� +: +先手取左边: +取完之后X>L>R +� +> +� +> +� +,那么后手在右边取相同数量的石子 +取完之后X==L +� +== +� +,那么后手把右边的石堆取完 +取完之后RL>R +� +> +� +> +� +,那么后手在左边取相同数量的石子 +取完之后RR>L +� +> +� +> +� +: + +先手取左边: +取完之后X>R>L +� +> +� +> +� +,那么后手在右边取相同数量的石子 +取完之后LR>L +� +> +� +> +� +,那么后手就在左边取相同的数量的石子 +取完之后L<=X - -using namespace std; -const int INF = 0x3f3f3f3f; -typedef long long LL; -const int N = 1e5 + 10; -int q[N]; -int n; -void quick_sort(int q[], int l, int r) { - if (l >= r) return; - int i = l - 1, j = r + 1, x = q[(l + r) >> 1]; - while (i < j) { - do i++; - while (q[i] < x); - do j--; - while (q[j] > x); - if (i < j) swap(q[i], q[j]); - } - quick_sort(q, l, j), quick_sort(q, j + 1, r); -} -int main() { - scanf("%d", &n); - for (int i = 1; i <= n; i++) scanf("%d", &q[i]); - quick_sort(q, 1, n); - for (int i = 1; i <= n; i++) printf("%d ", q[i]); - return 0; -} \ No newline at end of file diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/博弈论.md b/TangDou/AcWing_TiGao/T5/GameTheory/博弈论.md index fb6a442..440969d 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/博弈论.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/博弈论.md @@ -73,122 +73,7 @@ $$ [$HDU2176$](https://www.cnblogs.com/littlehb/p/16403990.html) -<<<<<<< HEAD [$HDU1730$](https://www.cnblogs.com/littlehb/p/16403933.html) -======= -int main() { - int n; - scanf("%d", &n); - - int res = 0; //起始值是0,因为任何数与0进行异或都它本身 - while (n--) { - int x; - scanf("%d", &x); - res ^= x; - } - if (res) - puts("Yes"); //异或值非零,必胜 - else puts("No"); //异或值是零,必败 - return 0; -} -``` - -经典$Nim$游戏 [$HDU1850$](https://acm.hdu.edu.cn/showproblem.php?pid=1850) - -$HDU1850$ 本题为一道**尼姆博弈**的改进题目,题目问题是给你几堆扑克;让你判断如果你能赢,你第一次操作都能使对手败的操作次数有多少种。根据题目大致可以根据其特点,得到如下思路;因为你每次能从任意选择一堆并取走其中的任意张牌。那么,只要看每一堆中有多少种操作;只要每一次中改变的不使之得到如下结果$a_i ∧ k -using namespace std; -const int N = 100 + 10; -int n, a[N], res; -int main() { - while (scanf("%d", &n) && n) { - res = 0; - for (int i = 1; i <= n; i++) { - scanf("%d", &a[i]); - res ^= a[i]; - } - //先手必改,没的选择,输出0 - if (res == 0) - puts("0"); - else { - //先手的人如果想赢,第一步有几种选择呢? - int ans = 0; - for (int i = 1; i <= n; i++) - if ((res ^ a[i]) < a[i]) ans++; - printf("%d\n", ans); - } - } - return 0; -} -``` - -[$HDU$2176](https://acm.hdu.edu.cn/showproblem.php?pid=2176) -> 先取者负输出$No$.先取者胜输出$Yes$,然后输出先取者第$1$次取子的所有方法.如果从有$a$个石子的堆中取若干个后剩下$b$个后会胜就输出$a$ $b$ - -```c++ -#include -using namespace std; -const int N = 200010; -int a[N]; - -int main() { - int n; - while (~scanf("%d", &n) && n) { - int x = 0; - for (int i = 0; i < n; i++) { - scanf("%d", &a[i]); - x = x ^ a[i]; - } - if (!x) - puts("No"); - else { - puts("Yes"); - for (int i = 0; i < n; i++) { - int res = a[i] ^ x; - //找到了证明中提到的: a_i ,此堆的数量最高位第k位,数值为1 - //拿走:a[i]- (a[i]^x) - //剩下:a[i]^x = res个 - if (res < a[i]) - printf("%d %d\n", a[i], res); - } - } - } - return 0; -} -``` - -经典$Nim$ -[$HDU$1730](https://acm.hdu.edu.cn/showproblem.php?pid=1730) - -> 思路:如果$2$个棋子相邻的话,那么就是一个棋子逃跑,一个棋子尾随,此时相邻的两个棋子是必败态,每次都是走一步,这个游戏有$n$行,所以我们可以把这个游戏看成是$n$堆的取石子游戏,每一堆的石子数即为两个棋子的间距,所以可以利用$Nim$博弈 - -```c++ -#include -using namespace std; -int main() { - int n, m; - while (~scanf("%d %d", &n, &m)) { - int res = 0; - for (int i = 1; i <= n; ++i) { - int a, b; - scanf("%d %d", &a, &b); - // 两个棋子的距离的绝对值减1.因为我们看当两个棋子相邻的时候, - // 已经是一个必败态了。不信我贴着你,你走吧。你走一步我跟一步,最后你将无路路可走 - res = res ^ (abs(a - b) - 1); - } - if (res == 0) - puts("BAD LUCK!"); - else - puts("I WIN!"); - } - return 0; -} -``` ->>>>>>> parent of ab3f165ae ('commit') --- From efa7c2ca36e0b6b4320408c1c10e7ce7da15e087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 26 Dec 2023 14:47:34 +0800 Subject: [PATCH 17/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1322.md | 26 +++++++++------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md index f252ff4..759e954 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md @@ -43,31 +43,27 @@ $1≤T≤10,1≤n≤1000,1≤a_i≤10^9$ 即:$(left[i][j],\underbrace{a_i,a_{i+1},\cdots,a_j}_{a[i]\sim a[j]})$,$(\underbrace{a_i,a_{i+1},\cdots,a_j}_{a[i]\sim a[j]},right[i][j])$ 为 **先手必败** 局面 -#### 2、神奇的性质 -① $left[i][j]$,$right[i][j]$一定存在 -② $left[i][j]$,$right[i][j]$必然唯一 - -##### (1) $left[i][j]$ 的唯一性证明 - +#### 2、性质1 $left[i][j]$,$right[i][j]$一定存在 **反证法**: -假设 $left(i,j)$ 不唯一,则存在非负整数 $x_1,x_2(x_1 \neq x_2)$,使得$(x_1,a_i,a_{i+1},⋯,a_{j−1},a_j)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 均为必败局面,而 **第一个必败局面** 可以通过拿走左侧$x_1-x_2$个石子到达另一个 **必败局面** ,矛盾,假设不成立,唯一性显然。 - -##### (2) $left[i][j]$ 的**存在性证明** -反证法: -假设不存在满足定义的 $left[i][j]$,则对于 **任意非负整数** $x$,有形如: +假设不存在满足定义的 $left[i][j]$,则对于 **任意正整数** $x$,有形如: $$\large \underbrace{x,a_i,a_{i+1},\cdots,a_j}_{A(x)}$$ 都为**必胜局面**,记为 $A(x)$ 局面。 -由于 $A(x)$ 为必胜局面,故从 $A(x)$ 局面 必然存在$M$种一步可达必败局面。 +由于 $A(x)$ 为必胜局面,故从 $A(x)$ 局面 必然存在若干种办法一步可达必败局面。 若从最左边一堆中拿,因为假设原因,不可能变成必败局面,因为这样得到的局面仍形如 $A(x)$。 -注意包括此行在内的接下来几行默认 $x \neq 0$ -左边拿没用,只能考虑从右边拿: +左边拿没用,只能考虑从右边拿(即从$a_j$里拿): 于是设 $A(x)$ 一步可达的(某个)**必败局面**为 $(x,a_i,a_{i+1},\cdots,a_{j-1},y)$,显然有 $0 \le y < a_j$。 -**由于 $x$ 有无限个,但 $y$ 只有 $a_j$种——根据抽屉原理,必存在 $x_1,x_2(x_1 \neq x_2),y$ 满足 $(x_1,a_i,a_{i+1},\cdots,a_{j-1},y)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},y)$ 都是必败局面**。但这两个必败局面之间 **实际一步可达**,故矛盾,进而原命题成立。 +**由于 $x$ 有无限个,但 $y$ 只有 $a_j$种——根据抽屉原理,必存在 $x_1,x_2(x_1 > x_2),y$ 满足 $(x_1,a_i,a_{i+1},\cdots,a_{j-1},y)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},y)$ 都是必败局面**。但这两个必败局面之间 **实际一步可达**(比如拿走$x_1-x_2$个),矛盾,假设不成,原命题成立。 + +#### 3、性质2 $left[i][j]$,$right[i][j]$必然唯一 + +**反证法**: +假设 $left(i,j)$ 不唯一,则存在非负整数 $x_1,x_2(x_1 \neq x_2)$,使得$(x_1,a_i,a_{i+1},⋯,a_{j−1},a_j)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 均为必败局面,而 **第一个必败局面** 可以通过拿走左侧$x_1-x_2$个石子到达另一个 **必败局面** ,矛盾,假设不成立,原命题成立。 + From ea36955eb85c4e809360147a348de253b961072e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 26 Dec 2023 14:58:54 +0800 Subject: [PATCH 18/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1322.md | 29 ++++++++++------------ 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md index 759e954..776f5e9 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md @@ -42,12 +42,13 @@ $1≤T≤10,1≤n≤1000,1≤a_i≤10^9$ 即:$(left[i][j],\underbrace{a_i,a_{i+1},\cdots,a_j}_{a[i]\sim a[j]})$,$(\underbrace{a_i,a_{i+1},\cdots,a_j}_{a[i]\sim a[j]},right[i][j])$ 为 **先手必败** 局面 +有如下两个性质: -#### 2、性质1 $left[i][j]$,$right[i][j]$一定存在 +#### 2、$left[i][j]$,$right[i][j]$一定存在 **反证法**: -假设不存在满足定义的 $left[i][j]$,则对于 **任意正整数** $x$,有形如: +假设不存在满足定义的 $left[i][j]$,则对于 **任意非负整数** $x$,有形如: -$$\large \underbrace{x,a_i,a_{i+1},\cdots,a_j}_{A(x)}$$ 都为**必胜局面**,记为 $A(x)$ 局面。 +$$\large \underbrace{x,a_i,a_{i+1},\cdots,a_j}_{A(x)}$$ 都为 **必胜局面** ,记为 $A(x)$ 局面。 由于 $A(x)$ 为必胜局面,故从 $A(x)$ 局面 必然存在若干种办法一步可达必败局面。 @@ -55,27 +56,23 @@ $$\large \underbrace{x,a_i,a_{i+1},\cdots,a_j}_{A(x)}$$ 都为**必胜局面**, 左边拿没用,只能考虑从右边拿(即从$a_j$里拿): -于是设 $A(x)$ 一步可达的(某个)**必败局面**为 $(x,a_i,a_{i+1},\cdots,a_{j-1},y)$,显然有 $0 \le y < a_j$。 -**由于 $x$ 有无限个,但 $y$ 只有 $a_j$种——根据抽屉原理,必存在 $x_1,x_2(x_1 > x_2),y$ 满足 $(x_1,a_i,a_{i+1},\cdots,a_{j-1},y)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},y)$ 都是必败局面**。但这两个必败局面之间 **实际一步可达**(比如拿走$x_1-x_2$个),矛盾,假设不成,原命题成立。 +于是设 $A(x)$ 一步可达的某个 **必败局面**为 $(x,a_i,a_{i+1},\cdots,a_{j-1},y)$,显然有 $0 \le y < a_j$。 -#### 3、性质2 $left[i][j]$,$right[i][j]$必然唯一 +**由于 $x$ 有无限个,但 $y$ 只有 $a_j$种——根据抽屉原理,必然存在 $x_1,x_2(x_1 > x_2),y$ 满足 $(x_1,a_i,a_{i+1},\cdots,a_{j-1},y)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},y)$ 都是必败局面**。但这两个必败局面之间 **实际一步可达**(比如拿走$x_1-x_2$个),矛盾,假设不成立,原命题成立。 + +#### 3、$left[i][j]$,$right[i][j]$必然唯一 **反证法**: 假设 $left(i,j)$ 不唯一,则存在非负整数 $x_1,x_2(x_1 \neq x_2)$,使得$(x_1,a_i,a_{i+1},⋯,a_{j−1},a_j)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 均为必败局面,而 **第一个必败局面** 可以通过拿走左侧$x_1-x_2$个石子到达另一个 **必败局面** ,矛盾,假设不成立,原命题成立。 - - - -### 五、状态转移 - -#### 1、边界情况 +#### 4、边界情况 $$\LARGE left[i][i]=a_i$$ 当只有一堆石子时,我在这堆前面添加一堆,个数和这堆一样多,对于**两堆相同的石子**,**后手进行和先手对称的操作**,你咋干我就咋干,我拿完,你瞪眼~, **先手必败** -#### 2、递推关系 +#### 5、递推关系 * 变化方法:从左侧拿走一些石子或者从右侧拿走一些石子 * 让我们使用$left[i][j-1]$和$right[i][j-1]$来表示$left[i][j]$和$right[i][j]$,形成$DP$递推关系 @@ -83,11 +80,11 @@ $$\LARGE left[i][i]=a_i$$ ![](http://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/2023/02/cccc1a0f80d1475adcf8096aa9e35ff0.png) -#### 3、推论 +#### 6、推论 有了上面推的$left[i][j]$唯一性,得出一个有用的推论: **对于任意非负整数 $x \neq left(i,j)$,$\large (x,a_i,a_{i+1},\cdots,a_j)$为必胜局面** -#### 4、特殊情况:$L=R=0$ +#### 7、特殊情况:$L=R=0$ 为方便叙述,下文记 $left[i][j-1]$ 为 $L$,记 $right[i][j-1]$ 为 $R$,并令 $\displaystyle \large x=a_j(x>0)$ @@ -96,7 +93,7 @@ $$\LARGE left[i][i]=a_i$$ 注:因$R=0$,表示在[$i$,$j-1$]确定后,右侧为$0$就能满足[$i$,$j-1$]这一段为先手必败,此时,左侧增加那堆个数为$0$就可以继续保持原来的先手必败,即$L=0$。 -#### 5、分类讨论 +#### 8、分类讨论 * $x=R$($Case$ $1$) 最简单的情况——根据 $R=right[i][j-1]$ 的定义,区间 $[i,j]$ 本来就是必败局面,因此左边啥也不能添,添了反而错,故 From 39bde88db1729a087d3e2aa3f7450495dfb1012a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 27 Dec 2023 10:53:33 +0800 Subject: [PATCH 19/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp | 77 +-- TangDou/AcWing_TiGao/T5/GameTheory/1322.md | 62 ++- TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md | 506 +++++-------------- 3 files changed, 228 insertions(+), 417 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp index 2a282ae..a10919f 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp @@ -1,40 +1,53 @@ -#include +#include + using namespace std; -const int N = 1e3 + 5; -int a[N], L[N][N], R[N][N]; + +const int N = 1010; + +int n; +int a[N]; +int left[N][N], right[N][N]; + int main() { int T; - cin >> T; + scanf("%d", &T); while (T--) { - int n; - cin >> n; - for (int i = 1; i <= n; i++) { - cin >> a[i]; - L[i][i] = R[i][i] = a[i]; - } - for (int len = 2; len <= n; len++) + scanf("%d", &n); + for (int i = 1; i <= n; i++) scanf("%d", &a[i]); + + for (int len = 1; len <= n; len++) for (int i = 1; i + len - 1 <= n; i++) { - int j = i + len - 1, l = L[i][j - 1], r = R[i][j - 1], x = a[j]; - if (x == r) - L[i][j] = 0; - else if (x >= l && x < r) - L[i][j] = x + 1; - else if (x > r && x <= l) - L[i][j] = x - 1; - else - L[i][j] = x; - - l = L[i + 1][j], r = R[i + 1][j], x = a[i]; - if (x == l) - R[i][j] = 0; - else if (x >= r && x < l) - R[i][j] = x + 1; - else if (x > l && x <= r) - R[i][j] = x - 1; - else - R[i][j] = x; + int j = i + len - 1; + if (len == 1) + left[i][j] = right[i][j] = a[i]; + else { + int L = left[i][j - 1], R = right[i][j - 1], X = a[j]; + if (R == X) + left[i][j] = 0; + else if (X < L && X < R || X > L && X > R) + left[i][j] = X; + else if (L > R) + left[i][j] = X - 1; + else + left[i][j] = X + 1; + + L = left[i + 1][j], R = right[i + 1][j], X = a[i]; + if (L == X) + right[i][j] = 0; + else if (X < L && X < R || X > L && X > R) + right[i][j] = X; + else if (R > L) + right[i][j] = X - 1; + else + right[i][j] = X + 1; + } } - puts(L[2][n] == a[1] ? "0" : "1"); + + if (n == 1) + puts("1"); + else + printf("%d\n", left[2][n] != a[1]); } + return 0; -} \ No newline at end of file +} diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md index 776f5e9..6de7731 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md @@ -66,37 +66,62 @@ $$\large \underbrace{x,a_i,a_{i+1},\cdots,a_j}_{A(x)}$$ 都为 **必胜局面** **反证法**: 假设 $left(i,j)$ 不唯一,则存在非负整数 $x_1,x_2(x_1 \neq x_2)$,使得$(x_1,a_i,a_{i+1},⋯,a_{j−1},a_j)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 均为必败局面,而 **第一个必败局面** 可以通过拿走左侧$x_1-x_2$个石子到达另一个 **必败局面** ,矛盾,假设不成立,原命题成立。 +#### 4、推论 +有了上面推的$left[i][j]$唯一性,得出一个有用的推论: +**对于任意非负整数 $x \neq left(i,j)$,$\large (x,a_i,a_{i+1},\cdots,a_j)$为必胜局面** + +#### 5、定义$L$和$R$ +因为下面的讨论中出现的$L$和$R$的含义不是很好理解,我们先把这个概念理清楚: + +![](http://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/2023/02/cccc1a0f80d1475adcf8096aa9e35ff0.png) + + +**$Q$**:为什么要这么定义$L$和$R$呢? +答:博弈论的题目都是可以通过动态规划来递推的。 +为什么呢?你想啊,最终的胜利,肯定不是从天下直接掉下来的,是一步步取得的,也就是前序状态会直接影响后面的状态,所以 +> **博弈论 $\Leftrightarrow $ 动态规划 $\Leftrightarrow $ 递归** + +递推嘛,就是类似于 **数学归纳法**,先求出初始状态是多少,然后假设$i \sim j-1$这段已经计算出$left[i][j-1],right[i][j-1]$了,现在想依赖于这两个数值推导出$left[i][j],right[i][j]$,怕写的太长麻烦,就定义了$L=left[i][j-1],R=right[i][j-1]$ + +**$Q$:那为什么是定义先手必败,而不是先手必胜呢?** +答:因为上面证明过定义先手必败的动态规划数组,是肯定存在并且是唯一的,这样才能正常计算啊。 + +考虑三个问题: +- ① 初始值 +- ② 答案在哪 +- ③ 递推式 +> **注:答案在哪,并不是和递推式相关,而是和状态表示相关,一定要注意** -#### 4、边界情况 -$$\LARGE left[i][i]=a_i$$ -当只有一堆石子时,我在这堆前面添加一堆,个数和这堆一样多,对于**两堆相同的石子**,**后手进行和先手对称的操作**,你咋干我就咋干,我拿完,你瞪眼~, **先手必败** +**① 初始值** +$\large L[i][i]=R[i][i]=a_i$ -#### 5、递推关系 +当只有一堆石子时($only$ $i$),我在这堆前面添加一堆,个数和这堆一样多,对于**两堆相同的石子**,**后手进行和先手对称的操作**,你咋干我就咋干,我拿完,你瞪眼~, **先手必败** + + +**② 答案在哪** +**先手必败** $\Leftrightarrow \ L[2][n]=a_1$ +> **解释**:如果$L[2][n]$与$a[1]$相等,就意味着本来挺好的$a[2] \sim a[n]$,结果,前面放上了一个$a[1]$,而加上的$a[1]$使得现在的局面必败,先手必败。 + + +**③ 递推式** * 变化方法:从左侧拿走一些石子或者从右侧拿走一些石子 * 让我们使用$left[i][j-1]$和$right[i][j-1]$来表示$left[i][j]$和$right[i][j]$,形成$DP$递推关系 - - > 前面动作都按要求整完了,问我们:本步骤,我们有哪些变化,根据这些变化,怎么样用前面动作积累下来的数据来完成本步骤数据变化的填充,这不就是动态规划吗? - -![](http://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/2023/02/cccc1a0f80d1475adcf8096aa9e35ff0.png) -#### 6、推论 -有了上面推的$left[i][j]$唯一性,得出一个有用的推论: -**对于任意非负整数 $x \neq left(i,j)$,$\large (x,a_i,a_{i+1},\cdots,a_j)$为必胜局面** -#### 7、特殊情况:$L=R=0$ -为方便叙述,下文记 $left[i][j-1]$ 为 $L$,记 $right[i][j-1]$ 为 $R$,并令 $\displaystyle \large x=a_j(x>0)$ +#### 分类讨论 +- **特殊情况:$L=R=0$** -若 $R=0$ 则 $L=R=0$,此时 $x>\max\{L,R\}$,也就是说 $L=0$ 和 $R=0$ 都属于 $Case$ $5$,故其它 $Case$ 满足 $L,R>0$。 +令 $\displaystyle \large x=a[j](x>0)$ -注:因$R=0$,表示在[$i$,$j-1$]确定后,右侧为$0$就能满足[$i$,$j-1$]这一段为先手必败,此时,左侧增加那堆个数为$0$就可以继续保持原来的先手必败,即$L=0$。 +若 $R=0$ 则 $L=R=0$,此时 $x>\max\{L,R\}$,也就是说 $L=0$ 和 $R=0$ 都属于 $Case$ $5$,故其它 $Case$ 满足 $L,R>0$。 -#### 8、分类讨论 +> 注:因$R=0$,表示在[$i$,$j-1$]确定后,右侧为$0$就能满足[$i$,$j-1$]这一段为先手必败,此时,左侧增加那堆个数为$0$就可以继续保持原来的先手必败,即$L=0$,而且已经证明了$L=R=0$是唯一的。 * $x=R$($Case$ $1$) - 最简单的情况——根据 $R=right[i][j-1]$ 的定义,区间 $[i,j]$ 本来就是必败局面,因此左边啥也不能添,添了反而错,故 + 最简单的情况——根据 $R=right[i][j-1]$ 的定义,$x=R$则区间 $[i,j]$ 是必败局面,因此左边啥也不能添,添了反而错,故 $$\large left[i][j]=0$$ * $xL$,则后手将最右堆拿成 $z-1$ 个石子($z-1 \ge L>0$),**保证左侧比右侧多$1$个石子**,就能回到 $Case$ $3$ 本身,递归证明即可 - * 若 $z=L$,则后手将最右堆拿完,根据 $L[i][j-1]$ 定义知此时局面必败 + * 若 $z=L$,则后手将最右堆拿完,根据 $L=left[i][j-1]$ 定义知此时局面必败 * 若 $0 @@ -178,7 +203,6 @@ $$ 同理可求 $R(i,j)$。 -回到原题,**先手必败当且仅当** $L[2][n]=a_1$ ,于是我们就做完啦! 时间复杂度 $O(n^2)$。 diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md index bb2fb98..2d86fae 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md @@ -33,369 +33,143 @@ $1≤T≤10,1≤n≤1000,1≤a_i≤10^9$ 0 ``` -### 解题思路 -首先定义两个状态: -① $left[i][j]$ : 我在$[i,j]$这个区间的左边放上一堆数量是多少的石头能够让我先手必败. -② $right[i][j]$: 我在$[i,j]$区间的左边放上一堆数量是多少的石头能让我先手必败. - -$left[i][j]$ 的性质:**必然存在且唯一**. - -**证明**:假设在区间左边放上$L_1,L_2$ 数量的石堆都能使我必败,不妨设$L_1R -� -> -� -且RR -� -> -� -,那么后手可以在另一边取先手取得数量少一的数量,这样对于每次先手取完之后剩余X>R -� -> -� -的情况来说,后手都可以在另一边取. -取完之后X=R -� -= -� -,那么就对应情况1,后手把另一边的石堆取光 -取完之后X=R -� ->= -� -,那么后手在另一边取数量加一的数量,对于每次先手取的情况,后手都可以在另一边取. -取完之后XL -� -> -� -且L<=X=L -� ->= -� -,后手就在另一边取数量加一的石头 -取完之后X=L+1 -� ->= -� -+ -1 -,后手就在另一边取数量减一的石头 -取完之后X<=L -� -<= -� -,后手就把右边的石头取完 -取完之后XL -� -> -� -且X>R -� -> -� -, left[i][j]=X -� -� -� -� -[ -� -] -[ -� -] -= -� -4.1: -X>L>R -� -> -� -> -� -: -先手取左边: -取完之后X>L>R -� -> -� -> -� -,那么后手在右边取相同数量的石子 -取完之后X==L -� -== -� -,那么后手把右边的石堆取完 -取完之后RL>R -� -> -� -> -� -,那么后手在左边取相同数量的石子 -取完之后RR>L -� -> -� -> -� -: - -先手取左边: -取完之后X>R>L -� -> -� -> -� -,那么后手在右边取相同数量的石子 -取完之后LR>L -� -> -� -> -� -,那么后手就在左边取相同的数量的石子 -取完之后L<=X必然存在若干种办法一步可达必败局面。 + +若从最左边一堆中拿,因为假设原因,不可能变成必败局面,因为这样得到的局面仍形如 $A(x)$。 + + +左边拿没用,只能考虑从右边拿(即从$a_j$里拿): + +于是设 $A(x)$ 一步可达的某个 **必败局面**为 $(x,a_i,a_{i+1},\cdots,a_{j-1},y)$,显然有 $0 \le y < a_j$。 + +**由于 $x$ 有无限个,但 $y$ 只有 $a_j$种——根据抽屉原理,必然存在 $x_1,x_2(x_1 > x_2),y$ 满足 $(x_1,a_i,a_{i+1},\cdots,a_{j-1},y)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},y)$ 都是必败局面**。但这两个必败局面之间 **实际一步可达**(比如拿走$x_1-x_2$个),矛盾,假设不成立,原命题成立。 + +#### 3、$left[i][j]$,$right[i][j]$必然唯一 + +**反证法**: +假设 $left(i,j)$ 不唯一,则存在非负整数 $x_1,x_2(x_1 \neq x_2)$,使得$(x_1,a_i,a_{i+1},⋯,a_{j−1},a_j)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 均为必败局面,而 **第一个必败局面** 可以通过拿走左侧$x_1-x_2$个石子到达另一个 **必败局面** ,矛盾,假设不成立,原命题成立。 + +#### 4、推论 +有了上面推的$left[i][j]$唯一性,得出一个有用的推论: +**对于任意非负整数 $x \neq left(i,j)$,$\large (x,a_i,a_{i+1},\cdots,a_j)$为必胜局面** + +#### 5、定义$L$和$R$ +因为下面的讨论中出现的$L$和$R$的含义不是很好理解,我们先把这个概念理清楚: + +![](http://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/2023/02/cccc1a0f80d1475adcf8096aa9e35ff0.png) + + +**$Q$**:为什么要这么定义$L$和$R$呢? +答:博弈论的题目都是可以通过动态规划来递推的。 +为什么呢?你想啊,最终的胜利,肯定不是从天下直接掉下来的,是一步步取得的,也就是前序状态会直接影响后面的状态,所以 +> **博弈论 $\Leftrightarrow $ 动态规划 $\Leftrightarrow $ 递归** + +递推嘛,就是类似于 **数学归纳法**,先求出初始状态是多少,然后假设$i \sim j-1$这段已经计算出$left[i][j-1],right[i][j-1]$了,现在想依赖于这两个数值推导出$left[i][j],right[i][j]$,怕写的太长麻烦,就定义了$L=left[i][j-1],R=right[i][j-1]$ + +$left[i][j]$递推 + +`?[i j-1] 第j堆(石子个数x)` +则我们希望求的是假设$i\sim j$已经固定了,我们在左边放多少个可以使得`?[i j-1] 第j堆` 是必败的 +定义: +左边放$L$时,`L[i j-1]`必败 +右边放$R$时,`[i j-1]R`必败 + + +**$Q$:那为什么是定义先手必败,而不是先手必胜呢?** +答:因为上面证明过定义先手必败的动态规划数组,是肯定存在并且是唯一的,这样才能正常计算啊。 + +考虑三个问题: +- ① 初始值 +- ② 答案在哪 +- ③ 递推式 +> **注:答案在哪,并不是和递推式相关,而是和状态表示相关,一定要注意** + + +**① 初始值** +$\large L[i][i]=R[i][i]=a_i$ + +当只有一堆石子时($only$ $i$),我在这堆前面添加一堆,个数和这堆一样多,对于**两堆相同的石子**,**后手进行和先手对称的操作**,你咋干我就咋干,我拿完,你瞪眼~, **先手必败** + + +**② 答案在哪** +**先手必败** $\Leftrightarrow \ L[2][n]=a_1$ +> **解释**:如果$L[2][n]$与$a[1]$相等,就意味着本来挺好的$a[2] \sim a[n]$,结果,前面放上了一个$a[1]$,而加上的$a[1]$使得现在的局面必败,先手必败。 + + +**③ 递推式** +* 变化方法:从左侧拿走一些石子或者从右侧拿走一些石子 +![](https://cdn.acwing.com/media/article/image/2022/04/04/145584_d2d0424bb4-%E6%97%A0%E6%A0%87%E9%A2%98.jpg) + + + +### 六、实现代码 +```cpp {.line-numbers} +#include + +using namespace std; +const int N = 1010; +int n; +int a[N], l[N][N], r[N][N]; + +int main() { + int T; + scanf("%d", &T); + while (T--) { + scanf("%d", &n); + for (int i = 1; i <= n; i++) scanf("%d", &a[i]); + + for (int len = 1; len <= n; len++) + for (int i = 1; i + len - 1 <= n; i++) { + int j = i + len - 1; + if (len == 1) + l[i][j] = r[i][j] = a[i]; + else { + int L = l[i][j - 1], R = r[i][j - 1], x = a[j]; + if (R == x) + l[i][j] = 0; + else if (x < L && x < R || x > L && x > R) + l[i][j] = x; + else if (L > R) + l[i][j] = x - 1; + else + l[i][j] = x + 1; + + // 与上述情况对称的四种情况 + L = l[i + 1][j], R = r[i + 1][j], x = a[i]; + if (L == x) + r[i][j] = 0; + else if (x < L && x < R || x > L && x > R) + r[i][j] = x; + else if (R > L) + r[i][j] = x - 1; + else + r[i][j] = x + 1; + } + } + + if (n == 1) + puts("1"); + else + printf("%d\n", l[2][n] != a[1]); + } + + return 0; +} +``` \ No newline at end of file From 8c90a04a94b9858da507dbf3dbae52ead6bb2c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 27 Dec 2023 11:26:34 +0800 Subject: [PATCH 20/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1322.md | 98 ++++++----- TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md | 175 ------------------- 2 files changed, 50 insertions(+), 223 deletions(-) delete mode 100644 TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md index 6de7731..4e47ff0 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md @@ -110,68 +110,70 @@ $\large L[i][i]=R[i][i]=a_i$ #### 分类讨论 +![](https://cdn.acwing.com/media/article/image/2022/04/04/145584_d2d0424bb4-%E6%97%A0%E6%A0%87%E9%A2%98.jpg) + - **特殊情况:$L=R=0$** -令 $\displaystyle \large x=a[j](x>0)$ +令 $\displaystyle \large X=a[j](x>0)$ 若 $R=0$ 则 $L=R=0$,此时 $x>\max\{L,R\}$,也就是说 $L=0$ 和 $R=0$ 都属于 $Case$ $5$,故其它 $Case$ 满足 $L,R>0$。 > 注:因$R=0$,表示在[$i$,$j-1$]确定后,右侧为$0$就能满足[$i$,$j-1$]这一段为先手必败,此时,左侧增加那堆个数为$0$就可以继续保持原来的先手必败,即$L=0$,而且已经证明了$L=R=0$是唯一的。 -* $x=R$($Case$ $1$) - 最简单的情况——根据 $R=right[i][j-1]$ 的定义,$x=R$则区间 $[i,j]$ 是必败局面,因此左边啥也不能添,添了反而错,故 +* $X=R$($Case$ $1$) + 最简单的情况——根据 $R=right[i][j-1]$ 的定义,$X=R$则区间 $[i,j]$ 是必败局面,因此左边啥也不能添,添了反而错,故 $$\large left[i][j]=0$$ -* $x - * $x \geq L$,即 $L \leq x < R$($Case$ $3$) - * **结论**:$$\large left[i][j]=x+1$$ + * ② $X \geq L$,即 $L \leq X < R$($Case$ $3$) + * **结论**:$$\large left[i][j]=X+1$$ * **证明**: - 即 **求证** $(x+1,a_i,a_{i+1},\cdots,a_{j-1},x)$为 **必败局面** ,其中 $L \leq x L$,则后手将最右堆拿成 $z-1$ 个石子($z-1 \ge L>0$),**保证左侧比右侧多$1$个石子**,就能回到 $Case$ $3$ 本身,递归证明即可 * 若 $z=L$,则后手将最右堆拿完,根据 $L=left[i][j-1]$ 定义知此时局面必败 * 若 $0 * 若先手拿最右边一堆,设拿了以后 **还剩 $z$ 个石子** * 若 $z \ge L$,则后手将最左堆拿成 $z+1$个石子,就能回到 $Case$ $3$ 本身,递归证明即可 * 若 $0R$ - * $x≤L$,即 $R < x \leq L$($Case$ $4$) - * 结论:$$\large left[i][j]=x-1$$ +* $X>R$ + * ① $X≤L$,即 $R < X \leq L$($Case$ $4$) + * 结论:$$\large left[i][j]=X-1$$ * **证明**: * 若先手拿最左边一堆,设拿了以后还剩 $z$ 个石子。 - * 若 $z \geq R$,则后手将最右堆拿成 $z+1$ 个石子,保证左侧比右侧多$1$个石子,就能回到 $Case$ $4$ 本身,递归证明即可。 + * 若 $z \geq R$,则后手将最右堆拿成 $z+1$ 个石子,保证左侧比右侧多$1$个石子,就能回到 $Case$ $4$ 本身,递归证明即可。 * 若 $0R$),由 $right[i][j-1])$ 的定义知此时是必败局面。
* 若先手拿最右边一堆,设拿了以后还剩 $z$ 个石子。 - * 若 $z>R$,则后手将最左边一堆拿成 $z-1$ 个石子(注意 $z-1 \ge R >0$),递归证明即可。保证右侧比左侧多$1$个石子。 + * 若 $z>R$,则后手将最左边一堆拿成 $z-1$ 个石子(注意 $z-1 \ge R >0$),递归证明即可。保证右侧比左侧多$1$个石子。 * 若 $z=R$,则后手把最左堆拿完,根据 $right[i][j-1]$的定义可知得到了必败局面。 * 若 $0 - * $x>L$,即 $x>\max\{L,R\}$($Case$ $5$) + * ② $X>L$,即 $X>\max\{L,R\}$($Case$ $5$) * **结论**:$$\large left[i][j]=x$$ * **证明**: 设先手将其中一堆拿成了 $z$ 个石子。 @@ -190,30 +192,30 @@ $\large L[i][i]=R[i][i]=a_i$ **综上所述:** $$ \large -L[i][j]= +left[i][j]= \large \left\{\begin{matrix} - 0 & x=R \\ - x+1&L \leq x < R \\ -x-1 & R温馨提示:**请看清楚 $L$ 取不取等,乱取等是错的!** - -同理可求 $R(i,j)$。 +> 温馨提示:**请看清楚 $L$ 取不取等,乱取等是错的!** - -时间复杂度 $O(n^2)$。 +同理可求 $right(i,j)$。 ### 六、实现代码 ```cpp {.line-numbers} #include using namespace std; + const int N = 1010; + int n; -int a[N], l[N][N], r[N][N]; +int a[N]; +int left[N][N], right[N][N]; int main() { int T; @@ -226,37 +228,37 @@ int main() { for (int i = 1; i + len - 1 <= n; i++) { int j = i + len - 1; if (len == 1) - l[i][j] = r[i][j] = a[i]; + left[i][j] = right[i][j] = a[i]; else { - int L = l[i][j - 1], R = r[i][j - 1], x = a[j]; - if (R == x) - l[i][j] = 0; - else if (x < L && x < R || x > L && x > R) - l[i][j] = x; + int L = left[i][j - 1], R = right[i][j - 1], X = a[j]; + if (R == X) + left[i][j] = 0; + else if (X < L && X < R || X > L && X > R) + left[i][j] = X; else if (L > R) - l[i][j] = x - 1; + left[i][j] = X - 1; else - l[i][j] = x + 1; - - // 与上述情况对称的四种情况 - L = l[i + 1][j], R = r[i + 1][j], x = a[i]; - if (L == x) - r[i][j] = 0; - else if (x < L && x < R || x > L && x > R) - r[i][j] = x; + left[i][j] = X + 1; + + L = left[i + 1][j], R = right[i + 1][j], X = a[i]; + if (L == X) + right[i][j] = 0; + else if (X < L && X < R || X > L && X > R) + right[i][j] = X; else if (R > L) - r[i][j] = x - 1; + right[i][j] = X - 1; else - r[i][j] = x + 1; + right[i][j] = X + 1; } } if (n == 1) puts("1"); else - printf("%d\n", l[2][n] != a[1]); + printf("%d\n", left[2][n] != a[1]); } return 0; } + ``` \ No newline at end of file diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md deleted file mode 100644 index 2d86fae..0000000 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md +++ /dev/null @@ -1,175 +0,0 @@ -##[$AcWing 1322$. 取石子游戏](https://www.acwing.com/problem/content/1324/) - -### 一、题目描述 -在研究过 $Nim$ 游戏及各种变种之后,$Orez$ 又发现了一种全新的取石子游戏,这个游戏是这样的: - -有 $n$ 堆石子,将这 $n$ 堆石子摆成一排。 - -游戏由两个人进行,两人轮流操作,每次操作者都可以从 **最左** 或 **最右** 的一堆中取出若干颗石子,可以将那一堆全部取掉,但不能不取,**不能操作的人就输了**。 - -$Orez$ 问:对于任意给出的一个初始局面,是否存在先手必胜策略。 - -**输入格式** -第一行为一个整数 $T$,表示有 $T$ 组测试数据。 - -对于每组测试数据,第一行为一个整数 $n$,表示有 $n$ 堆石子,第二行为 $n$ 个整数 $a_i$ ,依次表示每堆石子的数目。 - -**输出格式** -对于每组测试数据仅输出一个整数 $0$ 或 $1$,占一行。 - -其中 $1$ 表示有先手必胜策略,$0$ 表示没有。 - -**数据范围** -$1≤T≤10,1≤n≤1000,1≤a_i≤10^9$ - -**输入样例**: -```cpp {.line-numbers} -1 -4 -3 1 9 4 -``` -输出样例: -```cpp {.line-numbers} -0 -``` - -### 二、思考过程 - -#### 1、状态定义 - -① 设 $left[i][j]$ 表示在 $[i,j]$ 已经固定的区间 **左侧** 放上一堆数量为 $left[i][j]$ 的石子后,**先手必败** -② 设 $right[i][j]$ 表示在 $[i,j]$ 已经固定的区间 **右侧** 放上一堆数量为 $right[i][j]$ 的石子后,**先手必败** - -即:$(left[i][j],\underbrace{a_i,a_{i+1},\cdots,a_j}_{a[i]\sim a[j]})$,$(\underbrace{a_i,a_{i+1},\cdots,a_j}_{a[i]\sim a[j]},right[i][j])$ 为 **先手必败** 局面 - -有如下两个性质: - -#### 2、$left[i][j]$,$right[i][j]$一定存在 -**反证法**: -假设不存在满足定义的 $left[i][j]$,则对于 **任意非负整数** $x$,有形如: - -$$\large \underbrace{x,a_i,a_{i+1},\cdots,a_j}_{A(x)}$$ 都为 **必胜局面** ,记为 $A(x)$ 局面。 - -由于 $A(x)$ 为必胜局面,故从 $A(x)$ 局面 必然存在若干种办法一步可达必败局面。 - -若从最左边一堆中拿,因为假设原因,不可能变成必败局面,因为这样得到的局面仍形如 $A(x)$。 - - -左边拿没用,只能考虑从右边拿(即从$a_j$里拿): - -于是设 $A(x)$ 一步可达的某个 **必败局面**为 $(x,a_i,a_{i+1},\cdots,a_{j-1},y)$,显然有 $0 \le y < a_j$。 - -**由于 $x$ 有无限个,但 $y$ 只有 $a_j$种——根据抽屉原理,必然存在 $x_1,x_2(x_1 > x_2),y$ 满足 $(x_1,a_i,a_{i+1},\cdots,a_{j-1},y)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},y)$ 都是必败局面**。但这两个必败局面之间 **实际一步可达**(比如拿走$x_1-x_2$个),矛盾,假设不成立,原命题成立。 - -#### 3、$left[i][j]$,$right[i][j]$必然唯一 - -**反证法**: -假设 $left(i,j)$ 不唯一,则存在非负整数 $x_1,x_2(x_1 \neq x_2)$,使得$(x_1,a_i,a_{i+1},⋯,a_{j−1},a_j)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 均为必败局面,而 **第一个必败局面** 可以通过拿走左侧$x_1-x_2$个石子到达另一个 **必败局面** ,矛盾,假设不成立,原命题成立。 - -#### 4、推论 -有了上面推的$left[i][j]$唯一性,得出一个有用的推论: -**对于任意非负整数 $x \neq left(i,j)$,$\large (x,a_i,a_{i+1},\cdots,a_j)$为必胜局面** - -#### 5、定义$L$和$R$ -因为下面的讨论中出现的$L$和$R$的含义不是很好理解,我们先把这个概念理清楚: - -![](http://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/2023/02/cccc1a0f80d1475adcf8096aa9e35ff0.png) - - -**$Q$**:为什么要这么定义$L$和$R$呢? -答:博弈论的题目都是可以通过动态规划来递推的。 -为什么呢?你想啊,最终的胜利,肯定不是从天下直接掉下来的,是一步步取得的,也就是前序状态会直接影响后面的状态,所以 -> **博弈论 $\Leftrightarrow $ 动态规划 $\Leftrightarrow $ 递归** - -递推嘛,就是类似于 **数学归纳法**,先求出初始状态是多少,然后假设$i \sim j-1$这段已经计算出$left[i][j-1],right[i][j-1]$了,现在想依赖于这两个数值推导出$left[i][j],right[i][j]$,怕写的太长麻烦,就定义了$L=left[i][j-1],R=right[i][j-1]$ - -$left[i][j]$递推 - -`?[i j-1] 第j堆(石子个数x)` -则我们希望求的是假设$i\sim j$已经固定了,我们在左边放多少个可以使得`?[i j-1] 第j堆` 是必败的 -定义: -左边放$L$时,`L[i j-1]`必败 -右边放$R$时,`[i j-1]R`必败 - - -**$Q$:那为什么是定义先手必败,而不是先手必胜呢?** -答:因为上面证明过定义先手必败的动态规划数组,是肯定存在并且是唯一的,这样才能正常计算啊。 - -考虑三个问题: -- ① 初始值 -- ② 答案在哪 -- ③ 递推式 -> **注:答案在哪,并不是和递推式相关,而是和状态表示相关,一定要注意** - - -**① 初始值** -$\large L[i][i]=R[i][i]=a_i$ - -当只有一堆石子时($only$ $i$),我在这堆前面添加一堆,个数和这堆一样多,对于**两堆相同的石子**,**后手进行和先手对称的操作**,你咋干我就咋干,我拿完,你瞪眼~, **先手必败** - - -**② 答案在哪** -**先手必败** $\Leftrightarrow \ L[2][n]=a_1$ -> **解释**:如果$L[2][n]$与$a[1]$相等,就意味着本来挺好的$a[2] \sim a[n]$,结果,前面放上了一个$a[1]$,而加上的$a[1]$使得现在的局面必败,先手必败。 - - -**③ 递推式** -* 变化方法:从左侧拿走一些石子或者从右侧拿走一些石子 -![](https://cdn.acwing.com/media/article/image/2022/04/04/145584_d2d0424bb4-%E6%97%A0%E6%A0%87%E9%A2%98.jpg) - - - -### 六、实现代码 -```cpp {.line-numbers} -#include - -using namespace std; -const int N = 1010; -int n; -int a[N], l[N][N], r[N][N]; - -int main() { - int T; - scanf("%d", &T); - while (T--) { - scanf("%d", &n); - for (int i = 1; i <= n; i++) scanf("%d", &a[i]); - - for (int len = 1; len <= n; len++) - for (int i = 1; i + len - 1 <= n; i++) { - int j = i + len - 1; - if (len == 1) - l[i][j] = r[i][j] = a[i]; - else { - int L = l[i][j - 1], R = r[i][j - 1], x = a[j]; - if (R == x) - l[i][j] = 0; - else if (x < L && x < R || x > L && x > R) - l[i][j] = x; - else if (L > R) - l[i][j] = x - 1; - else - l[i][j] = x + 1; - - // 与上述情况对称的四种情况 - L = l[i + 1][j], R = r[i + 1][j], x = a[i]; - if (L == x) - r[i][j] = 0; - else if (x < L && x < R || x > L && x > R) - r[i][j] = x; - else if (R > L) - r[i][j] = x - 1; - else - r[i][j] = x + 1; - } - } - - if (n == 1) - puts("1"); - else - printf("%d\n", l[2][n] != a[1]); - } - - return 0; -} -``` \ No newline at end of file From 9f9f121a05762fbde3878483dd6f2d8a6b006ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 27 Dec 2023 11:28:05 +0800 Subject: [PATCH 21/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp | 34 ++++++++++---------- TangDou/AcWing_TiGao/T5/GameTheory/1322.md | 35 ++++++++++----------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp index a10919f..0b21f7e 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp @@ -1,4 +1,4 @@ -#include +#include using namespace std; @@ -6,47 +6,47 @@ const int N = 1010; int n; int a[N]; -int left[N][N], right[N][N]; +int l[N][N], r[N][N]; // left,right 在 iostream库中用过了,不能用! int main() { int T; - scanf("%d", &T); + cin >> T; while (T--) { - scanf("%d", &n); - for (int i = 1; i <= n; i++) scanf("%d", &a[i]); + cin >> n; + for (int i = 1; i <= n; i++) cin >> a[i]; for (int len = 1; len <= n; len++) for (int i = 1; i + len - 1 <= n; i++) { int j = i + len - 1; if (len == 1) - left[i][j] = right[i][j] = a[i]; + l[i][j] = r[i][j] = a[i]; else { - int L = left[i][j - 1], R = right[i][j - 1], X = a[j]; + int L = l[i][j - 1], R = r[i][j - 1], X = a[j]; if (R == X) - left[i][j] = 0; + l[i][j] = 0; else if (X < L && X < R || X > L && X > R) - left[i][j] = X; + l[i][j] = X; else if (L > R) - left[i][j] = X - 1; + l[i][j] = X - 1; else - left[i][j] = X + 1; + l[i][j] = X + 1; - L = left[i + 1][j], R = right[i + 1][j], X = a[i]; + L = l[i + 1][j], R = r[i + 1][j], X = a[i]; if (L == X) - right[i][j] = 0; + r[i][j] = 0; else if (X < L && X < R || X > L && X > R) - right[i][j] = X; + r[i][j] = X; else if (R > L) - right[i][j] = X - 1; + r[i][j] = X - 1; else - right[i][j] = X + 1; + r[i][j] = X + 1; } } if (n == 1) puts("1"); else - printf("%d\n", left[2][n] != a[1]); + printf("%d\n", l[2][n] != a[1]); } return 0; diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md index 4e47ff0..c14a9a9 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md @@ -207,7 +207,7 @@ $$ ### 六、实现代码 ```cpp {.line-numbers} -#include +#include using namespace std; @@ -215,50 +215,49 @@ const int N = 1010; int n; int a[N]; -int left[N][N], right[N][N]; +int l[N][N], r[N][N]; // left,right 在 iostream库中用过了,不能用! int main() { int T; - scanf("%d", &T); + cin >> T; while (T--) { - scanf("%d", &n); - for (int i = 1; i <= n; i++) scanf("%d", &a[i]); + cin >> n; + for (int i = 1; i <= n; i++) cin >> a[i]; for (int len = 1; len <= n; len++) for (int i = 1; i + len - 1 <= n; i++) { int j = i + len - 1; if (len == 1) - left[i][j] = right[i][j] = a[i]; + l[i][j] = r[i][j] = a[i]; else { - int L = left[i][j - 1], R = right[i][j - 1], X = a[j]; + int L = l[i][j - 1], R = r[i][j - 1], X = a[j]; if (R == X) - left[i][j] = 0; + l[i][j] = 0; else if (X < L && X < R || X > L && X > R) - left[i][j] = X; + l[i][j] = X; else if (L > R) - left[i][j] = X - 1; + l[i][j] = X - 1; else - left[i][j] = X + 1; + l[i][j] = X + 1; - L = left[i + 1][j], R = right[i + 1][j], X = a[i]; + L = l[i + 1][j], R = r[i + 1][j], X = a[i]; if (L == X) - right[i][j] = 0; + r[i][j] = 0; else if (X < L && X < R || X > L && X > R) - right[i][j] = X; + r[i][j] = X; else if (R > L) - right[i][j] = X - 1; + r[i][j] = X - 1; else - right[i][j] = X + 1; + r[i][j] = X + 1; } } if (n == 1) puts("1"); else - printf("%d\n", left[2][n] != a[1]); + printf("%d\n", l[2][n] != a[1]); } return 0; } - ``` \ No newline at end of file From 769a38c81b3f7b3860bbeb3a128e695f5f2cb6a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 27 Dec 2023 11:30:32 +0800 Subject: [PATCH 22/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1322.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md index c14a9a9..9d49c55 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md @@ -110,16 +110,12 @@ $\large L[i][i]=R[i][i]=a_i$ #### 分类讨论 -![](https://cdn.acwing.com/media/article/image/2022/04/04/145584_d2d0424bb4-%E6%97%A0%E6%A0%87%E9%A2%98.jpg) - - **特殊情况:$L=R=0$** +若 $R=0$ 则 $L=R=0$,此时 $x>\max\{L,R\}$,也就是说 $L=0$ 和 $R=0$ 都属于 $Case$ $5$,故其它 $Case$ 满足 $L,R>0$。 令 $\displaystyle \large X=a[j](x>0)$ - -若 $R=0$ 则 $L=R=0$,此时 $x>\max\{L,R\}$,也就是说 $L=0$ 和 $R=0$ 都属于 $Case$ $5$,故其它 $Case$ 满足 $L,R>0$。 - > 注:因$R=0$,表示在[$i$,$j-1$]确定后,右侧为$0$就能满足[$i$,$j-1$]这一段为先手必败,此时,左侧增加那堆个数为$0$就可以继续保持原来的先手必败,即$L=0$,而且已经证明了$L=R=0$是唯一的。 * $X=R$($Case$ $1$) From 50d2f9093a4f09c092f8ec5f86d045843182cf2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 27 Dec 2023 11:30:45 +0800 Subject: [PATCH 23/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp index 0b21f7e..5aef895 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp @@ -1,9 +1,7 @@ #include using namespace std; - const int N = 1010; - int n; int a[N]; int l[N][N], r[N][N]; // left,right 在 iostream库中用过了,不能用! From 539c7889f9c52e5a7383188bd7a59b337c7c67b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 28 Dec 2023 15:44:01 +0800 Subject: [PATCH 24/87] 'commit' --- TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp | 38 +++++------ TangDou/AcWing_TiGao/T5/GameTheory/1322.md | 72 +++++++++++++-------- 2 files changed, 63 insertions(+), 47 deletions(-) diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp index 5aef895..3956405 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp @@ -1,50 +1,50 @@ -#include +#include using namespace std; const int N = 1010; int n; int a[N]; -int l[N][N], r[N][N]; // left,right 在 iostream库中用过了,不能用! +int left[N][N], right[N][N]; // left,right 在 iostream库中用过了,不能用! int main() { int T; - cin >> T; + scanf("%d", &T); while (T--) { - cin >> n; - for (int i = 1; i <= n; i++) cin >> a[i]; + scanf("%d", &n); + for (int i = 1; i <= n; i++) scanf("%d", &a[i]); - for (int len = 1; len <= n; len++) - for (int i = 1; i + len - 1 <= n; i++) { + for (int len = 1; len <= n; len++) // 枚举长度 + for (int i = 1; i + len - 1 <= n; i++) { // left[i][j],从i到j int j = i + len - 1; if (len == 1) - l[i][j] = r[i][j] = a[i]; + left[i][j] = right[i][j] = a[i]; // DP初始值 else { - int L = l[i][j - 1], R = r[i][j - 1], X = a[j]; + int L = left[i][j - 1], R = right[i][j - 1], X = a[j]; if (R == X) - l[i][j] = 0; + left[i][j] = 0; else if (X < L && X < R || X > L && X > R) - l[i][j] = X; + left[i][j] = X; else if (L > R) - l[i][j] = X - 1; + left[i][j] = X - 1; else - l[i][j] = X + 1; + left[i][j] = X + 1; - L = l[i + 1][j], R = r[i + 1][j], X = a[i]; + L = left[i + 1][j], R = right[i + 1][j], X = a[i]; if (L == X) - r[i][j] = 0; + right[i][j] = 0; else if (X < L && X < R || X > L && X > R) - r[i][j] = X; + right[i][j] = X; else if (R > L) - r[i][j] = X - 1; + right[i][j] = X - 1; else - r[i][j] = X + 1; + right[i][j] = X + 1; } } if (n == 1) puts("1"); else - printf("%d\n", l[2][n] != a[1]); + printf("%d\n", left[2][n] != a[1]); } return 0; diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md index 9d49c55..80362d8 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md @@ -70,21 +70,18 @@ $$\large \underbrace{x,a_i,a_{i+1},\cdots,a_j}_{A(x)}$$ 都为 **必胜局面** 有了上面推的$left[i][j]$唯一性,得出一个有用的推论: **对于任意非负整数 $x \neq left(i,j)$,$\large (x,a_i,a_{i+1},\cdots,a_j)$为必胜局面** -#### 5、定义$L$和$R$ -因为下面的讨论中出现的$L$和$R$的含义不是很好理解,我们先把这个概念理清楚: - -![](http://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/2023/02/cccc1a0f80d1475adcf8096aa9e35ff0.png) +#### 5、疑问 +博弈论的题目都是可以通过动态规划来递推的。 -**$Q$**:为什么要这么定义$L$和$R$呢? -答:博弈论的题目都是可以通过动态规划来递推的。 -为什么呢?你想啊,最终的胜利,肯定不是从天下直接掉下来的,是一步步取得的,也就是前序状态会直接影响后面的状态,所以 -> **博弈论 $\Leftrightarrow $ 动态规划 $\Leftrightarrow $ 递归** +**$Q$:为什么定义先手必败,而不是定义先手必胜呢?** +答:因为上面证明过定义 **先手必败** 的动态规划结果数组,是肯定存在并且是唯一的.存在且唯一的,可以递推出来,如果定义的是 **先手必胜**,根据博弈论的知识,我们知道,必胜的策略不唯一,不方便递推。而如果我们采用的是 **先手必败** 这样的定义,那么由于它的存在性和唯一性,所以,只要不是它就是必胜局面! +**$Q$:怎么递推?** 递推嘛,就是类似于 **数学归纳法**,先求出初始状态是多少,然后假设$i \sim j-1$这段已经计算出$left[i][j-1],right[i][j-1]$了,现在想依赖于这两个数值推导出$left[i][j],right[i][j]$,怕写的太长麻烦,就定义了$L=left[i][j-1],R=right[i][j-1]$ -**$Q$:那为什么是定义先手必败,而不是先手必胜呢?** -答:因为上面证明过定义先手必败的动态规划数组,是肯定存在并且是唯一的,这样才能正常计算啊。 +![](http://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/2023/02/cccc1a0f80d1475adcf8096aa9e35ff0.png) + 考虑三个问题: - ① 初始值 @@ -93,50 +90,69 @@ $$\large \underbrace{x,a_i,a_{i+1},\cdots,a_j}_{A(x)}$$ 都为 **必胜局面** > **注:答案在哪,并不是和递推式相关,而是和状态表示相关,一定要注意** + **① 初始值** -$\large L[i][i]=R[i][i]=a_i$ +$\large left[i][i]=right[i][i]=a_i$ 当只有一堆石子时($only$ $i$),我在这堆前面添加一堆,个数和这堆一样多,对于**两堆相同的石子**,**后手进行和先手对称的操作**,你咋干我就咋干,我拿完,你瞪眼~, **先手必败** **② 答案在哪** -**先手必败** $\Leftrightarrow \ L[2][n]=a_1$ -> **解释**:如果$L[2][n]$与$a[1]$相等,就意味着本来挺好的$a[2] \sim a[n]$,结果,前面放上了一个$a[1]$,而加上的$a[1]$使得现在的局面必败,先手必败。 +**先手必败** $\Leftrightarrow \ left[2][n]==a[1]$ , **先手必胜** $\Leftrightarrow \ left[2][n]!=a[1]$ +> **解释**:$a[2] \sim a[n]$,前面放上了一个$a[1]$, +> 根据定义$left[2][n]$代表在$a[2]\sim a[n]$之前放上一个数,可以使得放后的局面必败。 +> 现在放上去的是$a[1]$,可以它偏偏等于$left[2][n]$这个令人讨厌的数字,面对这样的局面,天生是死局,先手必败。 + **③ 递推式** -* 变化方法:从左侧拿走一些石子或者从右侧拿走一些石子 -* 让我们使用$left[i][j-1]$和$right[i][j-1]$来表示$left[i][j]$和$right[i][j]$,形成$DP$递推关系 +* 变化方法:从左侧拿走一些石子或者从右侧拿走一些石子,我们需要考虑在一个局面确定后,在此局面上左侧、右侧添加一个什么数字(石子个数),才能使得变化后的局面必败。 + +* $left[i][j-1]$和$right[i][j-1]$表示$left[i][j]$和$right[i][j]$,形成$DP$递推关系 -#### 分类讨论 +递推式需要分类讨论 -- **特殊情况:$L=R=0$** -若 $R=0$ 则 $L=R=0$,此时 $x>\max\{L,R\}$,也就是说 $L=0$ 和 $R=0$ 都属于 $Case$ $5$,故其它 $Case$ 满足 $L,R>0$。 +先把 **特殊情况** 说清楚: -令 $\displaystyle \large X=a[j](x>0)$ +**$\large L=R=0$** +若 $R=0$ 则 $L=0$ > 注:因$R=0$,表示在[$i$,$j-1$]确定后,右侧为$0$就能满足[$i$,$j-1$]这一段为先手必败,此时,左侧增加那堆个数为$0$就可以继续保持原来的先手必败,即$L=0$,而且已经证明了$L=R=0$是唯一的。 +此时 $X>\max\{L,R\}$,也就是说 $L=0$ 和 $R=0$ 都属于 $Case$ $5$,故其它 $Case$ 满足 $L,R>0$。 + +令 $\displaystyle \large X=a[j](X>0)$ + +下面,我们按$X$与$R$的大小关系,划分为三种情况,分别进行讨论: +$$ +\large \left\{\begin{matrix} +X=R & \\ +XR & \left\{\begin{matrix} X \leq L \\X>L \end{matrix}\right. +\end{matrix}\right. +$$ + * $X=R$($Case$ $1$) - 最简单的情况——根据 $R=right[i][j-1]$ 的定义,$X=R$则区间 $[i,j]$ 是必败局面,因此左边啥也不能添,添了反而错,故 + 根据 $R=right[i][j-1]$ 的定义,$X=R$则区间 $[i,j]$ 是必败局面,因此左边啥也不能添,添了反而错 $$\large left[i][j]=0$$ * $X * ② $X \geq L$,即 $L \leq X < R$($Case$ $3$) - * **结论**:$$\large left[i][j]=X+1$$ + * **必胜策略**: + 当右侧石子个数为$X$时,$\large left[i][j]=X+1$.即在右侧石子个数确定为$X$后,如果在左侧添加一堆石子,个数为$X+1$,就可以保证当前局面先手必败。 * **证明**: 即 **求证** $(X+1,a_i,a_{i+1},\cdots,a_{j-1},X)$为 **必败局面** ,其中 $L \leq X R$ * ① $X≤L$,即 $R < X \leq L$($Case$ $4$) - * 结论:$$\large left[i][j]=X-1$$ + * **必胜策略**:$$\large left[i][j]=X-1$$ * **证明**: * 若先手拿最左边一堆,设拿了以后还剩 $z$ 个石子。 - * 若 $z \geq R$,则后手将最右堆拿成 $z+1$ 个石子,保证左侧比右侧多$1$个石子,就能回到 $Case$ $4$ 本身,递归证明即可。 + * 若 $z \geq R$,则后手将最右堆拿成 $z+1$ 个石子,保证左侧比右侧少$1$个石子,就能回到 $Case$ $4$ 本身,递归证明即可。 * 若 $0R$),由 $right[i][j-1])$ 的定义知此时是必败局面。 @@ -170,7 +186,7 @@ $\large L[i][i]=R[i][i]=a_i$
* ② $X>L$,即 $X>\max\{L,R\}$($Case$ $5$) - * **结论**:$$\large left[i][j]=x$$ + * **必胜策略**:$$\large left[i][j]=x$$ * **证明**: 设先手将其中一堆拿成了 $z$ 个石子。 * 若 $z>\max\{L,R\}$,后手将另一堆也拿成$z$个,回到 $Case$ $5$,递归证明。 From 701b4b58cb0ba7a002b9e7d8503da2c5fd312325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 28 Dec 2023 15:47:48 +0800 Subject: [PATCH 25/87] 'commit' --- .../AcWing/{MinimalPath => MiniPath}/1076.md | 0 .../{MinimalPath => MiniPath}/1076_1.cpp | 0 .../{MinimalPath => MiniPath}/1076_2.cpp | 0 .../AcWing/{MinimalPath => MiniPath}/1100.cpp | 0 .../AcWing/{MinimalPath => MiniPath}/1100.md | 0 .../AcWing/{MinimalPath => MiniPath}/1129.md | 0 .../AcWing/{MinimalPath => MiniPath}/1131.cpp | 0 .../AcWing/{MinimalPath => MiniPath}/1131.md | 0 .../{MinimalPath => MiniPath}/1134.drawio | 0 .../AcWing/{MinimalPath => MiniPath}/1134.md | 0 .../{MinimalPath => MiniPath}/1134_Bfs.cpp | 0 .../1134_Dijkstra.cpp | 0 .../AcWing/{MinimalPath => MiniPath}/1135.cpp | 0 .../AcWing/{MinimalPath => MiniPath}/1135.md | 0 .../AcWing/{MinimalPath => MiniPath}/1137.md | 0 .../{MinimalPath => MiniPath}/1137_1.cpp | 0 .../{MinimalPath => MiniPath}/1137_2.cpp | 0 .../AcWing/{MinimalPath => MiniPath}/188.cpp | 0 .../AcWing/{MinimalPath => MiniPath}/188.md | 0 .../AcWing/{MinimalPath => MiniPath}/188.xlsx | Bin .../AcWing/{MinimalPath => MiniPath}/340.cpp | 0 .../AcWing/{MinimalPath => MiniPath}/340.eddx | Bin .../AcWing/{MinimalPath => MiniPath}/340.md | 0 .../AcWing/{MinimalPath => MiniPath}/341.cpp | 0 .../AcWing/{MinimalPath => MiniPath}/341.md | 0 .../AcWing/{MinimalPath => MiniPath}/342.cpp | 0 .../AcWing/{MinimalPath => MiniPath}/342.md | 0 .../AcWing/{MinimalPath => MiniPath}/383.cpp | 0 .../AcWing/{MinimalPath => MiniPath}/383.md | 0 .../{MinimalPath => MiniPath}/CSP_J2.md | 0 .../AcWing/{MinimalPath => MiniPath}/P4568.md | 0 .../{MinimalPath => MiniPath}/P4568_1.cpp | 0 .../{MinimalPath => MiniPath}/P4568_2.cpp | 0 .../{MinimalPath => MiniPath}/POJ3464.cpp | 0 .../POJ上的奇奇怪怪的CompileError.md | 0 .../SPFA与BFS的区别.md | 0 .../AcWing/{MinimalPath => MiniPath}/Test.in | 0 .../AcWing/{MinimalPath => MiniPath}/Test.out | 0 .../{MinimalPath => MiniPath}/Test1.cpp | 0 .../{MinimalPath => MiniPath}/Test2.cpp | 0 .../AcWing/{MinimalPath => MiniPath}/Test2.in | 0 .../{MinimalPath => MiniPath}/Test2.out | 0 ...大小根堆、重载操作符的说明.md | 0 .../T3/MiniPath}/1126.cpp | 0 .../T3/MiniPath}/1126.md | 0 .../T3/MiniPath}/1127.cpp | 0 .../T3/MiniPath}/1127.md | 0 .../T3/MiniPath}/1128.cpp | 0 .../T3/MiniPath}/1128.in | 0 .../T3/MiniPath}/1128.md | 0 .../T3/MiniPath}/1129.cpp | 0 .../T3/MiniPath}/1129_Dijkstra.cpp | 0 .../T3/MiniPath}/1129_SPFA.cpp | 0 .../T3/MiniPath}/903.cpp | 0 .../T3/MiniPath}/903.md | 0 .../T3/MiniPath}/920.cpp | 0 .../T3/MiniPath}/920.md | 0 TangDou/AcWing_TiGao/T5/GameTheory/1322.md | 41 +++++++++--------- 58 files changed, 20 insertions(+), 21 deletions(-) rename TangDou/AcWing/{MinimalPath => MiniPath}/1076.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1076_1.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1076_2.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1100.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1100.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1129.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1131.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1131.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1134.drawio (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1134.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1134_Bfs.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1134_Dijkstra.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1135.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1135.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1137.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1137_1.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/1137_2.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/188.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/188.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/188.xlsx (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/340.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/340.eddx (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/340.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/341.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/341.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/342.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/342.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/383.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/383.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/CSP_J2.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/P4568.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/P4568_1.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/P4568_2.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/POJ3464.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/POJ上的奇奇怪怪的CompileError.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/SPFA与BFS的区别.md (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/Test.in (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/Test.out (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/Test1.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/Test2.cpp (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/Test2.in (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/Test2.out (100%) rename TangDou/AcWing/{MinimalPath => MiniPath}/关于优先队列priority_queue大小根堆、重载操作符的说明.md (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/1126.cpp (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/1126.md (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/1127.cpp (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/1127.md (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/1128.cpp (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/1128.in (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/1128.md (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/1129.cpp (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/1129_Dijkstra.cpp (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/1129_SPFA.cpp (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/903.cpp (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/903.md (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/920.cpp (100%) rename TangDou/{AcWing/MinimalPath => AcWing_TiGao/T3/MiniPath}/920.md (100%) diff --git a/TangDou/AcWing/MinimalPath/1076.md b/TangDou/AcWing/MiniPath/1076.md similarity index 100% rename from TangDou/AcWing/MinimalPath/1076.md rename to TangDou/AcWing/MiniPath/1076.md diff --git a/TangDou/AcWing/MinimalPath/1076_1.cpp b/TangDou/AcWing/MiniPath/1076_1.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1076_1.cpp rename to TangDou/AcWing/MiniPath/1076_1.cpp diff --git a/TangDou/AcWing/MinimalPath/1076_2.cpp b/TangDou/AcWing/MiniPath/1076_2.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1076_2.cpp rename to TangDou/AcWing/MiniPath/1076_2.cpp diff --git a/TangDou/AcWing/MinimalPath/1100.cpp b/TangDou/AcWing/MiniPath/1100.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1100.cpp rename to TangDou/AcWing/MiniPath/1100.cpp diff --git a/TangDou/AcWing/MinimalPath/1100.md b/TangDou/AcWing/MiniPath/1100.md similarity index 100% rename from TangDou/AcWing/MinimalPath/1100.md rename to TangDou/AcWing/MiniPath/1100.md diff --git a/TangDou/AcWing/MinimalPath/1129.md b/TangDou/AcWing/MiniPath/1129.md similarity index 100% rename from TangDou/AcWing/MinimalPath/1129.md rename to TangDou/AcWing/MiniPath/1129.md diff --git a/TangDou/AcWing/MinimalPath/1131.cpp b/TangDou/AcWing/MiniPath/1131.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1131.cpp rename to TangDou/AcWing/MiniPath/1131.cpp diff --git a/TangDou/AcWing/MinimalPath/1131.md b/TangDou/AcWing/MiniPath/1131.md similarity index 100% rename from TangDou/AcWing/MinimalPath/1131.md rename to TangDou/AcWing/MiniPath/1131.md diff --git a/TangDou/AcWing/MinimalPath/1134.drawio b/TangDou/AcWing/MiniPath/1134.drawio similarity index 100% rename from TangDou/AcWing/MinimalPath/1134.drawio rename to TangDou/AcWing/MiniPath/1134.drawio diff --git a/TangDou/AcWing/MinimalPath/1134.md b/TangDou/AcWing/MiniPath/1134.md similarity index 100% rename from TangDou/AcWing/MinimalPath/1134.md rename to TangDou/AcWing/MiniPath/1134.md diff --git a/TangDou/AcWing/MinimalPath/1134_Bfs.cpp b/TangDou/AcWing/MiniPath/1134_Bfs.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1134_Bfs.cpp rename to TangDou/AcWing/MiniPath/1134_Bfs.cpp diff --git a/TangDou/AcWing/MinimalPath/1134_Dijkstra.cpp b/TangDou/AcWing/MiniPath/1134_Dijkstra.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1134_Dijkstra.cpp rename to TangDou/AcWing/MiniPath/1134_Dijkstra.cpp diff --git a/TangDou/AcWing/MinimalPath/1135.cpp b/TangDou/AcWing/MiniPath/1135.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1135.cpp rename to TangDou/AcWing/MiniPath/1135.cpp diff --git a/TangDou/AcWing/MinimalPath/1135.md b/TangDou/AcWing/MiniPath/1135.md similarity index 100% rename from TangDou/AcWing/MinimalPath/1135.md rename to TangDou/AcWing/MiniPath/1135.md diff --git a/TangDou/AcWing/MinimalPath/1137.md b/TangDou/AcWing/MiniPath/1137.md similarity index 100% rename from TangDou/AcWing/MinimalPath/1137.md rename to TangDou/AcWing/MiniPath/1137.md diff --git a/TangDou/AcWing/MinimalPath/1137_1.cpp b/TangDou/AcWing/MiniPath/1137_1.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1137_1.cpp rename to TangDou/AcWing/MiniPath/1137_1.cpp diff --git a/TangDou/AcWing/MinimalPath/1137_2.cpp b/TangDou/AcWing/MiniPath/1137_2.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1137_2.cpp rename to TangDou/AcWing/MiniPath/1137_2.cpp diff --git a/TangDou/AcWing/MinimalPath/188.cpp b/TangDou/AcWing/MiniPath/188.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/188.cpp rename to TangDou/AcWing/MiniPath/188.cpp diff --git a/TangDou/AcWing/MinimalPath/188.md b/TangDou/AcWing/MiniPath/188.md similarity index 100% rename from TangDou/AcWing/MinimalPath/188.md rename to TangDou/AcWing/MiniPath/188.md diff --git a/TangDou/AcWing/MinimalPath/188.xlsx b/TangDou/AcWing/MiniPath/188.xlsx similarity index 100% rename from TangDou/AcWing/MinimalPath/188.xlsx rename to TangDou/AcWing/MiniPath/188.xlsx diff --git a/TangDou/AcWing/MinimalPath/340.cpp b/TangDou/AcWing/MiniPath/340.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/340.cpp rename to TangDou/AcWing/MiniPath/340.cpp diff --git a/TangDou/AcWing/MinimalPath/340.eddx b/TangDou/AcWing/MiniPath/340.eddx similarity index 100% rename from TangDou/AcWing/MinimalPath/340.eddx rename to TangDou/AcWing/MiniPath/340.eddx diff --git a/TangDou/AcWing/MinimalPath/340.md b/TangDou/AcWing/MiniPath/340.md similarity index 100% rename from TangDou/AcWing/MinimalPath/340.md rename to TangDou/AcWing/MiniPath/340.md diff --git a/TangDou/AcWing/MinimalPath/341.cpp b/TangDou/AcWing/MiniPath/341.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/341.cpp rename to TangDou/AcWing/MiniPath/341.cpp diff --git a/TangDou/AcWing/MinimalPath/341.md b/TangDou/AcWing/MiniPath/341.md similarity index 100% rename from TangDou/AcWing/MinimalPath/341.md rename to TangDou/AcWing/MiniPath/341.md diff --git a/TangDou/AcWing/MinimalPath/342.cpp b/TangDou/AcWing/MiniPath/342.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/342.cpp rename to TangDou/AcWing/MiniPath/342.cpp diff --git a/TangDou/AcWing/MinimalPath/342.md b/TangDou/AcWing/MiniPath/342.md similarity index 100% rename from TangDou/AcWing/MinimalPath/342.md rename to TangDou/AcWing/MiniPath/342.md diff --git a/TangDou/AcWing/MinimalPath/383.cpp b/TangDou/AcWing/MiniPath/383.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/383.cpp rename to TangDou/AcWing/MiniPath/383.cpp diff --git a/TangDou/AcWing/MinimalPath/383.md b/TangDou/AcWing/MiniPath/383.md similarity index 100% rename from TangDou/AcWing/MinimalPath/383.md rename to TangDou/AcWing/MiniPath/383.md diff --git a/TangDou/AcWing/MinimalPath/CSP_J2.md b/TangDou/AcWing/MiniPath/CSP_J2.md similarity index 100% rename from TangDou/AcWing/MinimalPath/CSP_J2.md rename to TangDou/AcWing/MiniPath/CSP_J2.md diff --git a/TangDou/AcWing/MinimalPath/P4568.md b/TangDou/AcWing/MiniPath/P4568.md similarity index 100% rename from TangDou/AcWing/MinimalPath/P4568.md rename to TangDou/AcWing/MiniPath/P4568.md diff --git a/TangDou/AcWing/MinimalPath/P4568_1.cpp b/TangDou/AcWing/MiniPath/P4568_1.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/P4568_1.cpp rename to TangDou/AcWing/MiniPath/P4568_1.cpp diff --git a/TangDou/AcWing/MinimalPath/P4568_2.cpp b/TangDou/AcWing/MiniPath/P4568_2.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/P4568_2.cpp rename to TangDou/AcWing/MiniPath/P4568_2.cpp diff --git a/TangDou/AcWing/MinimalPath/POJ3464.cpp b/TangDou/AcWing/MiniPath/POJ3464.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/POJ3464.cpp rename to TangDou/AcWing/MiniPath/POJ3464.cpp diff --git a/TangDou/AcWing/MinimalPath/POJ上的奇奇怪怪的CompileError.md b/TangDou/AcWing/MiniPath/POJ上的奇奇怪怪的CompileError.md similarity index 100% rename from TangDou/AcWing/MinimalPath/POJ上的奇奇怪怪的CompileError.md rename to TangDou/AcWing/MiniPath/POJ上的奇奇怪怪的CompileError.md diff --git a/TangDou/AcWing/MinimalPath/SPFA与BFS的区别.md b/TangDou/AcWing/MiniPath/SPFA与BFS的区别.md similarity index 100% rename from TangDou/AcWing/MinimalPath/SPFA与BFS的区别.md rename to TangDou/AcWing/MiniPath/SPFA与BFS的区别.md diff --git a/TangDou/AcWing/MinimalPath/Test.in b/TangDou/AcWing/MiniPath/Test.in similarity index 100% rename from TangDou/AcWing/MinimalPath/Test.in rename to TangDou/AcWing/MiniPath/Test.in diff --git a/TangDou/AcWing/MinimalPath/Test.out b/TangDou/AcWing/MiniPath/Test.out similarity index 100% rename from TangDou/AcWing/MinimalPath/Test.out rename to TangDou/AcWing/MiniPath/Test.out diff --git a/TangDou/AcWing/MinimalPath/Test1.cpp b/TangDou/AcWing/MiniPath/Test1.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/Test1.cpp rename to TangDou/AcWing/MiniPath/Test1.cpp diff --git a/TangDou/AcWing/MinimalPath/Test2.cpp b/TangDou/AcWing/MiniPath/Test2.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/Test2.cpp rename to TangDou/AcWing/MiniPath/Test2.cpp diff --git a/TangDou/AcWing/MinimalPath/Test2.in b/TangDou/AcWing/MiniPath/Test2.in similarity index 100% rename from TangDou/AcWing/MinimalPath/Test2.in rename to TangDou/AcWing/MiniPath/Test2.in diff --git a/TangDou/AcWing/MinimalPath/Test2.out b/TangDou/AcWing/MiniPath/Test2.out similarity index 100% rename from TangDou/AcWing/MinimalPath/Test2.out rename to TangDou/AcWing/MiniPath/Test2.out diff --git a/TangDou/AcWing/MinimalPath/关于优先队列priority_queue大小根堆、重载操作符的说明.md b/TangDou/AcWing/MiniPath/关于优先队列priority_queue大小根堆、重载操作符的说明.md similarity index 100% rename from TangDou/AcWing/MinimalPath/关于优先队列priority_queue大小根堆、重载操作符的说明.md rename to TangDou/AcWing/MiniPath/关于优先队列priority_queue大小根堆、重载操作符的说明.md diff --git a/TangDou/AcWing/MinimalPath/1126.cpp b/TangDou/AcWing_TiGao/T3/MiniPath/1126.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1126.cpp rename to TangDou/AcWing_TiGao/T3/MiniPath/1126.cpp diff --git a/TangDou/AcWing/MinimalPath/1126.md b/TangDou/AcWing_TiGao/T3/MiniPath/1126.md similarity index 100% rename from TangDou/AcWing/MinimalPath/1126.md rename to TangDou/AcWing_TiGao/T3/MiniPath/1126.md diff --git a/TangDou/AcWing/MinimalPath/1127.cpp b/TangDou/AcWing_TiGao/T3/MiniPath/1127.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1127.cpp rename to TangDou/AcWing_TiGao/T3/MiniPath/1127.cpp diff --git a/TangDou/AcWing/MinimalPath/1127.md b/TangDou/AcWing_TiGao/T3/MiniPath/1127.md similarity index 100% rename from TangDou/AcWing/MinimalPath/1127.md rename to TangDou/AcWing_TiGao/T3/MiniPath/1127.md diff --git a/TangDou/AcWing/MinimalPath/1128.cpp b/TangDou/AcWing_TiGao/T3/MiniPath/1128.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1128.cpp rename to TangDou/AcWing_TiGao/T3/MiniPath/1128.cpp diff --git a/TangDou/AcWing/MinimalPath/1128.in b/TangDou/AcWing_TiGao/T3/MiniPath/1128.in similarity index 100% rename from TangDou/AcWing/MinimalPath/1128.in rename to TangDou/AcWing_TiGao/T3/MiniPath/1128.in diff --git a/TangDou/AcWing/MinimalPath/1128.md b/TangDou/AcWing_TiGao/T3/MiniPath/1128.md similarity index 100% rename from TangDou/AcWing/MinimalPath/1128.md rename to TangDou/AcWing_TiGao/T3/MiniPath/1128.md diff --git a/TangDou/AcWing/MinimalPath/1129.cpp b/TangDou/AcWing_TiGao/T3/MiniPath/1129.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1129.cpp rename to TangDou/AcWing_TiGao/T3/MiniPath/1129.cpp diff --git a/TangDou/AcWing/MinimalPath/1129_Dijkstra.cpp b/TangDou/AcWing_TiGao/T3/MiniPath/1129_Dijkstra.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1129_Dijkstra.cpp rename to TangDou/AcWing_TiGao/T3/MiniPath/1129_Dijkstra.cpp diff --git a/TangDou/AcWing/MinimalPath/1129_SPFA.cpp b/TangDou/AcWing_TiGao/T3/MiniPath/1129_SPFA.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/1129_SPFA.cpp rename to TangDou/AcWing_TiGao/T3/MiniPath/1129_SPFA.cpp diff --git a/TangDou/AcWing/MinimalPath/903.cpp b/TangDou/AcWing_TiGao/T3/MiniPath/903.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/903.cpp rename to TangDou/AcWing_TiGao/T3/MiniPath/903.cpp diff --git a/TangDou/AcWing/MinimalPath/903.md b/TangDou/AcWing_TiGao/T3/MiniPath/903.md similarity index 100% rename from TangDou/AcWing/MinimalPath/903.md rename to TangDou/AcWing_TiGao/T3/MiniPath/903.md diff --git a/TangDou/AcWing/MinimalPath/920.cpp b/TangDou/AcWing_TiGao/T3/MiniPath/920.cpp similarity index 100% rename from TangDou/AcWing/MinimalPath/920.cpp rename to TangDou/AcWing_TiGao/T3/MiniPath/920.cpp diff --git a/TangDou/AcWing/MinimalPath/920.md b/TangDou/AcWing_TiGao/T3/MiniPath/920.md similarity index 100% rename from TangDou/AcWing/MinimalPath/920.md rename to TangDou/AcWing_TiGao/T3/MiniPath/920.md diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md index 80362d8..7a240e4 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md @@ -219,57 +219,56 @@ $$ ### 六、实现代码 ```cpp {.line-numbers} -#include +#include using namespace std; - const int N = 1010; - int n; int a[N]; -int l[N][N], r[N][N]; // left,right 在 iostream库中用过了,不能用! +int left[N][N], right[N][N]; // left,right 在 iostream库中用过了,不能用! int main() { int T; - cin >> T; + scanf("%d", &T); while (T--) { - cin >> n; - for (int i = 1; i <= n; i++) cin >> a[i]; + scanf("%d", &n); + for (int i = 1; i <= n; i++) scanf("%d", &a[i]); - for (int len = 1; len <= n; len++) - for (int i = 1; i + len - 1 <= n; i++) { + for (int len = 1; len <= n; len++) // 枚举长度 + for (int i = 1; i + len - 1 <= n; i++) { // left[i][j],从i到j int j = i + len - 1; if (len == 1) - l[i][j] = r[i][j] = a[i]; + left[i][j] = right[i][j] = a[i]; // DP初始值 else { - int L = l[i][j - 1], R = r[i][j - 1], X = a[j]; + int L = left[i][j - 1], R = right[i][j - 1], X = a[j]; if (R == X) - l[i][j] = 0; + left[i][j] = 0; else if (X < L && X < R || X > L && X > R) - l[i][j] = X; + left[i][j] = X; else if (L > R) - l[i][j] = X - 1; + left[i][j] = X - 1; else - l[i][j] = X + 1; + left[i][j] = X + 1; - L = l[i + 1][j], R = r[i + 1][j], X = a[i]; + L = left[i + 1][j], R = right[i + 1][j], X = a[i]; if (L == X) - r[i][j] = 0; + right[i][j] = 0; else if (X < L && X < R || X > L && X > R) - r[i][j] = X; + right[i][j] = X; else if (R > L) - r[i][j] = X - 1; + right[i][j] = X - 1; else - r[i][j] = X + 1; + right[i][j] = X + 1; } } if (n == 1) puts("1"); else - printf("%d\n", l[2][n] != a[1]); + printf("%d\n", left[2][n] != a[1]); } return 0; } + ``` \ No newline at end of file From c5fd755614cee0cda6904feefb64f7a55a6d784c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 28 Dec 2023 15:49:12 +0800 Subject: [PATCH 26/87] 'commit' --- .../T3/MiniPathYingYong}/1135.cpp | 0 .../T3/MiniPathYingYong}/1135.md | 0 .../T3/MiniPathYingYong}/340.cpp | 0 .../T3/MiniPathYingYong}/340.eddx | Bin .../T3/MiniPathYingYong}/340.md | 0 .../T3/MiniPathYingYong}/341.cpp | 0 .../T3/MiniPathYingYong}/341.md | 0 .../T3/MiniPathYingYong}/342.cpp | 0 .../T3/MiniPathYingYong}/342.md | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathYingYong}/1135.cpp (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathYingYong}/1135.md (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathYingYong}/340.cpp (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathYingYong}/340.eddx (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathYingYong}/340.md (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathYingYong}/341.cpp (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathYingYong}/341.md (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathYingYong}/342.cpp (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathYingYong}/342.md (100%) diff --git a/TangDou/AcWing/MiniPath/1135.cpp b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.cpp similarity index 100% rename from TangDou/AcWing/MiniPath/1135.cpp rename to TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.cpp diff --git a/TangDou/AcWing/MiniPath/1135.md b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.md similarity index 100% rename from TangDou/AcWing/MiniPath/1135.md rename to TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.md diff --git a/TangDou/AcWing/MiniPath/340.cpp b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.cpp similarity index 100% rename from TangDou/AcWing/MiniPath/340.cpp rename to TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.cpp diff --git a/TangDou/AcWing/MiniPath/340.eddx b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.eddx similarity index 100% rename from TangDou/AcWing/MiniPath/340.eddx rename to TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.eddx diff --git a/TangDou/AcWing/MiniPath/340.md b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.md similarity index 100% rename from TangDou/AcWing/MiniPath/340.md rename to TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.md diff --git a/TangDou/AcWing/MiniPath/341.cpp b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/341.cpp similarity index 100% rename from TangDou/AcWing/MiniPath/341.cpp rename to TangDou/AcWing_TiGao/T3/MiniPathYingYong/341.cpp diff --git a/TangDou/AcWing/MiniPath/341.md b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/341.md similarity index 100% rename from TangDou/AcWing/MiniPath/341.md rename to TangDou/AcWing_TiGao/T3/MiniPathYingYong/341.md diff --git a/TangDou/AcWing/MiniPath/342.cpp b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.cpp similarity index 100% rename from TangDou/AcWing/MiniPath/342.cpp rename to TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.cpp diff --git a/TangDou/AcWing/MiniPath/342.md b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md similarity index 100% rename from TangDou/AcWing/MiniPath/342.md rename to TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md From 245013263201e930df97e6ffed6066004c17bf64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 28 Dec 2023 16:07:26 +0800 Subject: [PATCH 27/87] 'commit' --- .../AcWing_TiGao/T3/MiniPathYingYong/1135.cpp | 26 ++++++------ .../T3/MiniPathYingYong/1135.eddx | Bin 0 -> 12913 bytes .../AcWing_TiGao/T3/MiniPathYingYong/1135.md | 38 +++++++++--------- 3 files changed, 33 insertions(+), 31 deletions(-) create mode 100644 TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.eddx diff --git a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.cpp b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.cpp index 841e270..df720b8 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.cpp +++ b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.cpp @@ -15,7 +15,7 @@ void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } -int dist[6][N]; +int dis[6][N]; int id[6]; // 0号索引:佳佳的家,其它5个亲戚,分别下标为1~5,值为所在的车站编号 /* @@ -26,13 +26,13 @@ bool st[N]; /* S:出发车站编号 - dist[]:是全局变量dist[6][N]的某一个二维,其实是一个一维数组 + dis[]:是全局变量dis[6][N]的某一个二维,其实是一个一维数组 C++的特点:如果数组做参数传递的话,将直接修改原地址的数据 此数组传值方式可以让我们深入理解C++的二维数组本质:就是多个一维数组,给数组头就可以顺序找到其它相关数据 - 计算的结果:获取到S出发到其它各个站点的最短距离,记录到dist[S][站点号]中 + 计算的结果:获取到S出发到其它各个站点的最短距离,记录到dis[S][站点号]中 */ -void dijkstra(int S, int dist[]) { - dist[S] = 0; +void dijkstra(int S, int dis[]) { + dis[S] = 0; memset(st, false, sizeof st); priority_queue, greater> q; q.push({0, S}); @@ -45,9 +45,9 @@ void dijkstra(int S, int dist[]) { st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; - if (dist[v] > dist[u] + w[i]) { - dist[v] = dist[u] + w[i]; - q.push({dist[v], v}); + if (dis[v] > dis[u] + w[i]) { + dis[v] = dis[u] + w[i]; + q.push({dis[v], v}); } } } @@ -66,10 +66,10 @@ void dfs(int u, int pre, int sum) { for (int i = 1; i <= 5; i++) // 在当前位置上,枚举每个可能出现在亲戚站点 if (!st[i]) { // 如果这个亲戚没走过 st[i] = true; // 走它 - // 本位置填充完,下一个位置,需要传递前序是i,走过的路径和是sum+dist[pre][id[i]].因为提前打好表了,所以肯定是最小值,直接用就行了  + // 本位置填充完,下一个位置,需要传递前序是i,走过的路径和是sum+dis[pre][id[i]].因为提前打好表了,所以肯定是最小值,直接用就行了  // 需要注意的是一维是 6的上限,也就是 佳佳家+五个亲戚 ,而不是 车站号(佳佳家+五个亲戚) !因为这样的话,数据就很大,数组开起来麻烦,可能会MLE // 要注意学习使用小的数据标号进行事情描述的思想 - dfs(u + 1, i, sum + dist[pre][id[i]]); + dfs(u + 1, i, sum + dis[pre][id[i]]); st[i] = false; // 回溯 } } @@ -91,10 +91,10 @@ int main() { // 计算从某个亲戚所在的车站出发,到达其它几个点的最短路径 // 因为这样会产生多组最短距离,需要一个二维的数组进行存储 - memset(dist, 0x3f, sizeof dist); - for (int i = 0; i < 6; i++) dijkstra(id[i], dist[i]); + memset(dis, 0x3f, sizeof dis); + for (int i = 0; i < 6; i++) dijkstra(id[i], dis[i]); // 枚举每个亲戚所在的车站站点,多次Dijkstra,分别计算出以id[i]这个车站出发,到达其它点的最短距离,相当于打表 - // 将结果距离保存到给定的二维数组dist的第二维中去,第一维是指从哪个车站点出发的意思 + // 将结果距离保存到给定的二维数组dis的第二维中去,第一维是指从哪个车站点出发的意思 // dfs还要用这个st数组做其它用途,所以,需要再次的清空 memset(st, 0, sizeof st); diff --git a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.eddx b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.eddx new file mode 100644 index 0000000000000000000000000000000000000000..1a75bb9b14bced54c8981d440d7d144b587b88d3 GIT binary patch literal 12913 zcmb_@bx&G=a-y2n`_0em6GTsmA)GC^L9C3+|KF-!bF-v!_B3>nAyv5VBl5u)cAmR3THc!q} z9$4PI=*-XWC@phC-nSclMKQX|=%oo6Kz9s$LLdF9=|Fq-y<{++KTHuxF8%N`yoiSD z#8JBDCjU@W6dEuNsLo!=<7sYh+d1_;j1u0x z&ZN&@a(9BDZxg`pWNGQ#`<{0!wtWo6ygSDmQPxu2OpwXbQKCV~OaazYP^9%laa)HZ z1Xi`|MMIq3x#5cjfLp~?742A!Q|HzdNjpjL6MU4aKI-@wyy-XR$&Riq!g|kr|M&nc z&)D)g<<%HSgm?|B@u%-40QW=+65)^|CgG2B-gUx@f#*@m+Y=w{XE8#`{}%6Fx7_D0$HoHYaY<61Rr7~Pvn3uAYooi$re zPu9Q~Kv_iO6szc1Jvkhu7lP~l(s9ij8V41>f{X}*{Ob;t8DelNs#Eb}a#HqfzbCew z?0YBH7}=`80=wE;NY>A+y)wu0)5RA-jktE~HKhmjG> z&5(dBtcJI!ohGgL z#5vEcE4Aulu_Eob^o88YzM0HXwz-;;?YP{gMyD$?;~Bz3bZhfZ#UG2m!g{A)yWTIH zZeGi-E;_ay?QVAZ1}-2dpE67KksU-{dNUHSghJC(t#v4BcVD`>e@LH`D0b~$v$nMr z){P_kv(ZeHr=zW8wM!99*YSBznh`Sc8(?;`3v4-*Oy|V}|7ZtPbe+3??=tPVR(v;p z=n*7RcwiQ>dT3w{C~;n*|18ui`O%(SJ&S;BEtOUVw@YDgklc3&Ie3K)UKR7G2zZ*F5u`7 zWXgp;Ypy`zY>1u`YXy|SMS{;si^Q_*iwP1;zlJSl!!+CQVMMRJP4Cn-I&6cK_zi4a zjc&}S%~GyfsUey$B1TnTqatwa^7f;bDKD}qwOZuPpNEshH>SAw0)9pk!O-W& z=TQCB)Nn$TgZWJDId~&ce#2w3P+lZjzPgxMFNV%=UL|`7IF1WtQ~E23HAgSC708Od zaIFo#R`AhZk^QUSc{|V*U4zn-NVB5?&sSzafMfWKjQAi+T3Y$)k{b&3Rl)FOphAML ziP73e!)`IH9~jxqtYL#TTd))s%6MV%i(+%ZZlbaKrU73+&N0Q09i~} zzq0XqJ@jR>1AiLv36G*zAh%M9&J;TZhL%I49!-5U>6eXeH}RhW%DHClRy~y3>Ei_k z0b+eWbHO)kx4#DLGpAa-U$K;$_8b611374Z*p3^JggC%L zF16>QBgP{PO#=oom5U|=+=1dGnBs3tmOPKnOE*K?w`q<(Y=b7t0DlESTO=3#rWpqp zi_k&>4D5yO+~JX@7lI2|aD{L4?vt#^4C)YV9UlC|DuqUxNViIx*w8bO5%!y7%O5lQ zxNAwN6yczsjosYw4-UdY45Go92Uky7o1#lUO)px2!qL=-(saQCK`1#26GcL^eKvM< zn#~peZZE{cpE?Gw^>q;G4vr5(w+K4)s|T;{=hcj(ml%Uu+*=}D%+;M$*2LvuUhG+; z|0-@Kk}d_8ma?BjvD_&@*q(rAlJR@VihY*i+qf2>BJnIr##9{+mtz%x$v8&?QcO+{nyS;N3E=#tORT9&W4>VoJl^A7pweunpLXIg$hw_PxJO&){V+y zVm@|?gbdwaXtECN7rq}3Hc}oCEk(87PH(qbH>Vh2XKFEguXWOe}ELM23zNHfbV`wz& zE4ap-^S3SIDogCY6W)bIo#tyFti<^+fhLr=rRmKPah0g4H2$Znf^`z5i!m9e&v&@& zKf5+#vo@V0#-5EJMN`2z5ut0UvEwV4J}z0+RY4~%!hHbsN7u(9%5D67@Rv7g)2y9g zmW^L{R#tu9L35H5^DAowS;L4ttsFh0Z24iXjezSIp_UpP`NJpmil5h@!zg-VE(pRJ zt#&w&)5@Z}%{Vb&$pq{+DdoUkql>cUBwRET$+u99z8@?&4{}sO%Aw(&GiudhrNhzD zyTM*pXw_^dBdT@)aX|T&X!zm2*3j1Wcay!%ChI4d^l4Q~7$iZIyJx~cW>v6&&f(>^ zSyE#$k4HOt+EtfeSj}1#+F^iLg}hCG9{Ed&zsvnAd=+` zy&860|G`Ih$rHln$!WhuX4wk8YIFRx}9$?YJ4m?UVj}aeLgPUefEEQX=jK3==_?Zu9=?do>zpDndZVP;Pi8G@maR5 z%4f)2wN2A;!XKecgi$d*G=XBmD?V^c48XpIube8@5Wg)?mCSFzoMq+E$B2B%t0>;K zq@)#On^Xc|qqSs==gsI}0u{fwoj54ALz5t3Cd9;7i%Bh)kjR8^ zZh(1{=G@eLI3**~ZoAwV6>-lEQvfHBcu%}kF5Sk$8F!kFiO-JAwx9*y31AtsL>RX5 zU4m$UTXs{QQ!?Xz9lM0FYLhR;Ol)vDV|pZt_gzDTGsINs)EN+mk#^4(pv6S$JkT<* z6GTn+ixavo13dyZ{#jL3pnbX+0rd22$c0Uv`u-L$(xSo?$i{!N#g}n>WIfLn7+m=A z8nAd3ix*Ek(Bim^yrXT~l>|e5(*cg zrJ=d3L4@xcsdtcj2l>_Sy&m1k2d_hkivVU1#_w5#b~DDrg``?&rJa$zGhy$e?)V}X z$|qX;4jW@?FjbCKT}Ez^x3>uy-h$Eqm<0pHiw5#mN7a_&AqyS~GlluxKUnGKMY!*; znH_yQQmCH>iUNSw(_!-0j@q0skX15Nq}R%JaWVe7{%w!}|M*(GLZwN%k!cOTV;*kZ z&(Graju+ZW^_^*4q=t2FG5IIFm?p~;^m+`;)z&8dMjMeTZ6uANE!v34AG)|jflI6H z%`WzxHW@~ya#1RtUq&*;dd))LZhl~VFYjPy!$ZFD6BgmUiBoikJ6T+H#nz==?N_{q z$1hhFEY9m~D#(E!Be!`iOVw@nd@VDZqdUwPpV6|X9V)ZD(OSiAjhncK%I+g*oIB;G z@R@ZXA&Hqw9Iz#h*;c^+62g?fWtz|L!kn=M3wEth%fZIzn+)t$BoiZbFQYa z6+(U#>bigWi-HxxxCZ*V|6p@&Mh7E}7(4+m40Bk&h=x39==+Rv)Yl*=S_WxD5qJdE z)D}nBK^P#l*nSy25qLG3f690kcA-oPJHOF-e%$5ce7=H~uY9OncQu8t z`y0{xacmB``w&d&;q)}G`?*taeiGtv;`r#O+uCQMvrKdQhlBD{$!$DyO2ld zW)BgbAmLeb+p<%EkxV-hDnJ`4Hwc9+*y}aR-;r$qShvwlWe)hM9Q;6ztUj(U%=Zip z7^Q2|CHoUJ*S+hfqRRs8oJ^uaj$~_(4T!LCFu)uVd0C57-kUXR4+YKAjQz|w>6%n4 zu+KOJ7qQ$GRt9K+i6r?=tO%Pc3diaje)*DT16V0BT|7bDD0AFVbQf&iXm38nhZMOD z8p5|l*7|}>u^mzw13}QmJ?iT$yrbI9UCM>H2!)8r!lLrkU3x&s{`k~x=Lp*zeC%Q@)(3|WOR?7y$(u26?=?3+^VGAe18 zi>9>)T5g1MiKf%2p|`1qL`2o=K+5> z7JyppU{Y4d>K)Dn<;CDYD~>K z*Rj}|bDE)=|Hcufx!i;Syf*28YF(E=OnaiT3G~qJyDnknjZdIc zN7;txJeLcj{s=~{3b9$gMm1X{+yb$A2vtqp&IliCtwN=km;$(a=x-U{Q*y)yJlE`N zXZqeSPM0ra_SVv&?&x4nLv7O`_O3pHa;$$h@l2VII$tx+HU?!LaYVgte=gqtyk4pd zutqYXt+T#h_6>*30wS%1XLTYBbe{tHyJrV$5_WvMskVvrzUDG_mH2iqe|d$d3rKuN~-jV-nJ> zlv@l)b17Bstf&M5fd`8bH=_WFM~)rwR$?~0cgRHu0#nqRqp*C02VdewS)qJWR$ruL zseAQrIiZ7T;YnA$iy8a|L^RM35pSWy+_0i*w-8@vA6~zpuahi;mDl#fji3N>0dE1~ zW_#100ph>!*Z?86zvk&y!DexDg~63u!NZ*ggk&0dcpM&IDjK$X_}_A#{{A$_fXqVA zwC|BobStVpz7z52OVI&OZsh~j0)eAkLudp7yfuwYtj2Eu1Cv$y*C5)bV}RD7l6>% zTZm0rCb2vF<2Ef`6(U|%SeKHr7*~eTLN#JMZ^*(xmhW%=*qTt17}GH|zXi|0oU#+L zvKiYBt=O09pPQn-#+uCsSq!yPMUTnKWs>2GMmye01Ic*P8x<$Li-!v%!sbtsf2Ry7 zuGgP=|5AnjB;>!z!NkguS^ppV4`bL+S2H@LLL6WT#{pz{_c;M;U0AV8X$`{<9a1|MBu)yyX9PSE=u=evOb|U`8Zh zU~qq3IhmPQoBS91`F+Y}K^V20{06z=V1p{!h%fm^c_T-@YJe=pjU&e+Z{2An`A88X zH4{G=`YE_gC?pO%j1BBoabhRb+YyiAl7?_zQIZQv-3U8h!`;**FX(Xih6QX!h2v4o zO2niK9=u=i__fuxZ(v2+vNx)u#g@|pl0)3c0cv!|WyeoI96Phq)8pol{0rX`L+Pn< zZK0L6A*!VR)6q%F*qhq)x-#i8vK2Rf{Z3Z8s)>9XY|q+u=J41nucfU0MKzg{hUT%EDI0+%|>Iy+H`Y z$Y`(vI=1IDR1w#}HA9X@qwDKg7i8YY=U#uyaeiu$&6~*AR1a$M{5%5aZh9jHB1Bb7 z88ESH>GPy$DEa3PRnGd64nfH_r4quJxNVX_^^E%J1Vd?Oh1HgGv;%9HUpkYmrX#H3 zEO#aGGworof;JK=u7f->Yro!c1`QuPW=g0<$YmwtW#Yy$D zu1fpPuTFC3$7_;G)T35Vp)CDiaD#)$ipBpv1()g{l%AA^+Ov1Oyw4T(}5#K2KGG{N2Apw@BLIEyxl z5ze@z9Ci+M<)%7~H|bM%=RehBol=g`-&C3%+1FBRevgFnTFX1w+=ysUIwI+Ym|I#n zjc=|*1zk)LsMEnmU(1)3GLzQ&tE$MILRv?g?&2`>KzaQ6tPuAmROp!XHzalNI8G&u zdu*^FjCLhGK=b?noi9JlB)HU2ZRu1oBfK(nEr%9I^)&=@*3S_Q|*%G=QspzY_2^*sw?s$^tGTj~68!1wnpzmFdnqu7IzJ7}l zY8(8z=Ogw>(q=57)A8#HdzRDqD3?O$Deht6DnFK1n)B{@*13EHPznL8Yb{mX~9uLQjg zOAr23w+vn%m7VJek!pJ8hjj8*Qfe2KSZg(Vt>euFYZaC;I zKJt!hJPF3Tx?YXlW|U)K)wG}EQ=w|+IcPBsnpMdUL2CnZ1=np_;n;S4#gO!|UY77l z_=kEV_^s3aTI&)C$w_iOV}63`gxc55$d43rOym3lp0|5uG%YUsKh^eiCw;|~Bbh1R zzoyaQg@zGbnaWh{j1pN!HO1LY*ju5Z4lIE1A1bhD{Feh!A4hAJm}J22C#^MyU!6kwuZmg_jfxaIG&Pph^)|g zBundktdUI00~xuYhGQv$GV*KlNGC9-Y1n!u?Jw?|&q^>CZj@^=HTSXzhP2RG2ggvgYrnth#^Jm>Q<3j_Dx&m00 z9uP>CqEtb;_LoJM8*s&9MA1Z1_mG|o9bE+^{;N!WqXDT7Gt@=VGrYLg!FJPS6hDp* zj8u{>&rIe>n+JVZ<*etx5r3L zK)S28*IP!8a!4d+qIH|ARWuET5x)i7)* za7B`B>@tK0oI^o|O?t`osOX}+qbaTkpD#qgPLGPVN!UA#f%gZ4oy>Bw$h4!4%}QLJ zbN1A0yew@d&`yO(kx!^07Lb2{ij~P^Y(lm#UcoLGsEDP&?za+n^pkj%UeL09=V=X-bF%m^xTAF1Dr}JGpn`*_eo3+f7Z@|Le(BQH9^+d z0Hi~@5J@<4cM3DMhWSIPg|AKdGjTG${htl>tphQ3k>sQF{af^r8`Vc%y&DAZxoBlu z$eIy)S9((M%$+oaXRk`LLnk={Dn`uTW=(DK)}P||(PK`S(!h}+!RrBH=AEF%xQ^Cn z4(duHb{;Ipop(f8JGy~ttTdtsh-4Ilaru#@vF&9kHbhNiHQ6rm>eY|p^-4|@Og&|% z%!Pdzw~}8SoH|t7&A-=$&tq~rvyibWzUC`HL80qaU!oCHQaPqB+ zWI&u#b5^Hc33&&oCJ?$zy?+=kJaEnA*BXiOMg`wI=@h6>d%lV)>Z)e*sW^4bR+VBb#VwRz4BTt^T%~yO=Xnq8X;=7M=Q@axVp5dRH~`?nOA0tZj!tu-Kd8J)s0` zMkuM#!n^!4JB~PJgA;-2tNsSa`UzU@zz5HD?0)K0a3c^eVvGH`id)lL+wejEGCT#K5>ds$$kLnx|r_5#XYi)vM>hD)J%~DJU2;5p z=1}<7i_61>B-xfk4(76jxl3ncG+oBZbCUi!L~Tlh-l#<#wQa(x3Yh`RG;XPox2xeW zwK# z{B?*yK6n1{Q1&>Ng>!T8dv?@gg6J?ahTnZX_AMc;n>^O~&lGGolN|gL_E;Hquhng- zCzE?F&@lhL2(TlID0Q-h4gtFd5OXx>3-S`O!_!tiD2@Jhpz@*rMN5)`U6m2KYlq|K zJ+#lO*yo7Ia{WE8nZ3ig6mt#UM7!@#DXiOArSH6Mv^zlI1;iztGqF}xLIpfqeAH)Wae5K1IO&`Q|S6&v0YzmIGRxCMtVBcAX1=>PLCf*6K@*tal!8e1{Kxns_9Gx_|V05b$ z={cZXpO@A^kouh$JG7?%8y_41MtI{9z$hwDXcrIcP-_FY{n-Drf)j{vTvvsx*pO45 ziU(st$&u483>6sL#9=U5)01k?lBz!)8W?jhhh< zy;8Ss8IMj}>3;6bl|5_KYz(tfm;kbstJ2nXNO!S*v07nA>~CMAC&6sX#ZSfyTh?Ek ztePxSvY~=OAVs{-zrPBz#q`n3F29KuLv@+S6_}`|IdyN#vU@bs&K@0O{HcwIj~+=s z`^&_k%_`@12p(|nwwFDPzfe*)WTk{>+BPIB2Xllm8jg9Eb<3s08-1qH>&Z(Z;xdES zIzL7{=1+g_^eY6ii11AmQ zwWA?hjB2c>1;HrgiXbkFKyc8kteXDgLk~8N1&l)rC-;in^*f&g?d?vt_lk%`A=*Mp z2n0^s0(05h%RJ`8`kd?np$WcRwCw`)2fXw=ry|&_diOcMyw%Id2yz&cQ#i7LM)sh6 znxYo_>5F|DG| z6XRjRqeP(|4%1C3)S`s{0!MiDQkT72)eko~ILgPS^@A&84ze0J-@jSHXhp_U4eAN8 zr0<91#%F*-CuWQ;IoG{h?g=wa`lI=-p?^8CpO?D5ww@#2$aIT-sq-0SUtvA`b^;~MBz&(^y0fO~FW+wtcnEm3&Vz7CP--SbiA^?a^V zVubA}LZO^-FeDB<<$9_jZ0H2aIgbCJ9vj`hs9;}e+>5`Qp{?V7I0l{na#*DSot?6w zLky1FmJ0Xzx)_W)p{^jVrU--V@j;HD{Sk6V11@x>n()ZIsvp5f@c}S}4-|zz0#$k| zc}iK-Sp!vd9?d=;9)g|a2o8kY%2gL< zVv4Ft9Rt{e-HG_FRAc!SjF=75w??VPa%p9eFT?3EpEV=;Zl;L7=o&Aq0z#1(}RC*Bo3_anfq}IHEhd$*XW^c z>T>(Vl}wU76ipX~PgO2aqpZzYRU=0zJyLdk3|!@GpX z>x1T4AT^sXb$GnyuA!ZOJjfEzojk#^E z3Z1G5o6CeV&N-?E_PMaVxz*OYS=E~YyV;?0T)2wnrIzKGVb6MH=b<3lwKE0I3jhZS zZ+Hr02nCxlZB%c5iU@)m=5QcOND< zKC-aXs^MP>Y%v>mYxLlt3tlR_|E$_pe3JJMfh#%LP;L z+kOn`^{lCeBfVM>Z^ZtmqN6sx2vd6L`Ghbn7rjq9aAz!!qe`i*DbjGyTZt~U&9(yT zXC1XmwjH|QQ+~gskja6tVW?%jn5?Pje{hT2Ns6tA2FBjxFaz=j{?4#MqB=)*%QZ9L z;>T(`xeXE(B%C~IfqAk9>iV_)g_c(i;Y->Mh?p1QyrL1dz%2PQ<`HoaYlNvbgNDi&E5816&UQ*-i8q|ZykkX zWIhe#50!2BFzuZ2D3nC}&be!Kdn}jgSP(#$azCX%B_rCB$w(0~azaWSV}k5No!QXC zZ^Igx`!eh4PMmg1i#jHVT|-5nTjgEG1nuQUmCNRl6RG#fvVr@j390N9KD0xjj}r=1 z$%Wme}DiuN% zwgvTvsr`f$6~1<5`?z)!rx%GhE@QxDkJyJ*pE>ucaa~6b`y|YG0XL2GF_w}*on1dc z`O+NO$u)Bw!^6>gq?$S)`MHWiBMdihCcVpGU=*bIJ9s?Inev{AGRYOMis$y)#-8`5pm8k36gJ~l))7lPg3d(TI%}rj$HeqX zfsgL&qkQSh#{R*1RP=&<(I6srDS{q#_?M8qg6|)2x%cb?nOyN_8DxS?zJ^`B){d{S z9T56a+Oi2ZUgn&$+9z8|L9m#ReKn$?BVXJoDrB*KfmICawm_9~#JAGa@ltbrqM8K+ zyt>(&wyGTT>BvH(-awbQQ2I} zXCk^Z^^rW9Y>Kj!QN&1!ZhU)@{3-VA?{_za=cKbA@1v?f1%05M@Mr;=^WB_gv+OMTuuJ%@I!HB>QqnFazEG$BD> z`qTuMx}KA&8fHeC4-3J(C+^>7xcRovZ*A?FwL4{bjq43(^StSGCn`=uho)-vM}gq+ z*-j0{zA{B!BdJd%)#sr3slyT0ZSrSHu9mnAZ%j(Uju36SKwNKh(fQ<7CV#?%EwpYZ z9Yxsho^hIGpLaf(pCLXk~4iV)L@1+impkKeD0;>W4zTC@8zF&@oizv{D`C{ zLzQ2a6z3}GJM9D(1^sUNk@(v-X>^oVDPhOW^w~e}%cCX3q58&0^Jc8}KkFq3gDd-u_!z?I<3~%0d{B;n!6BXzm z(lZS5Nrd>Ep`!Bqyi%3}hropVPx}}CQFH$LX!iT(`mehg0P0X+Fdg(!CjT4a-HAf&70&`FEArL{{({ZU)t3Fd`;N@fc(28_8*b{UE})K=G^~9 r`oD_Tf2{xSMEbwhpTYm9{=d@d{{jL9{ZE6T|DIWY`>vbipWOceiy!tL literal 0 HcmV?d00001 diff --git a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.md b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.md index ddb4a8b..bb233b8 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.md +++ b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/1135.md @@ -57,22 +57,24 @@ $id[1]=8$ 表示第$1$个亲戚家住在$8$号车站附近,记录每个亲戚与 #### 2、思考过程 -① 必须由佳佳的家出发,也就出发点肯定是$1$号车站 -② 现在想求佳佳去$5$个亲戚家,每一家都需要走到,不能漏掉任何一家,但顺序可以任意。这里要要用一个关系数组$id[]$来把亲戚家的编号与车站号挂接一下。 -③ 看到是最短路径问题,而且权值是正整数,考虑唯一可能性就是$Dijkstra$。 +① 必须由佳佳的家出发,也就是出发点肯定是$1$号车站 +② 现在想求佳佳去$5$个亲戚家,每一家都需要走到,不能漏掉任何一家,但顺序可以任意。这里要用一个关系数组$id[]$来把亲戚家的编号与车站号挂接一下。 +③ 看到是最短路径问题,而且权值是正整数,考虑$Dijkstra$。 ④ 但$Dijkstra$只能是单源最短路径求解,比如佳佳去二姨家,最短路径是多少。佳佳去三舅家,最短路径是多少。本题不是问某一家,问的是佳佳全去到,总的路径和最短是多少,这样的话,直接使用$Dijkstra$就无效了。 -⑤ 继续思考:因为亲戚家只有$5$个,可以从这里下手,通过全排列的办法,枚举出所有的可能顺序,此时,计算次数=$5*4*3*2*1=120$次。 就算是跑个$120$次的$Dijkstra$也不是啥大问题,就是常数大一点呗,可以试试。 +⑤ 继续思考:因为亲戚家只有$5$个,可以从这里下手,通过全排列的办法,枚举出所有的可能顺序,此时,计算次数=$5*4*3*2*1=120$次。 ⑥ 跑多次$Dijkstra$是在干什么呢?就是在分别以二姨,三舅,四大爷家为出发点,分别计算出到其它亲戚家的最短距离,如果我们把顺序分别枚举出来,每次查一下已经预处理出来的两个亲戚家的最短距离,再加在一起,不就是可以进行$PK$最小值了吗? 至此,整体思路完成。 +![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202312281603744.png) + #### 3.编码步骤 * **$6$次最短路** 分别以佳佳家、五个亲戚家为出发点($id[i]~ i\in[0,5]$),求$6$次最短路,相当于打表,一会要查 * **求全排列** - 因为佳佳所有的亲戚都要拜访到,现在不知道的是什么样顺序拜访才是时间最少的。 把所有可能顺序都 **枚举** 出来,通过查表,找出两个亲戚家之间的最小时间,累加结果的和,再$PJ$最小就是答案 + 因为佳佳所有的亲戚都要拜访到,现在不知道的是什么样顺序拜访才是时间最少的。 把所有可能顺序都 **枚举** 出来,通过查表,找出两个亲戚家之间的最小时间,累加结果的和,再$PK$最小就是答案 #### 4.实现细节 通过前面的$6$次打表预处理,可以求出$6$个$dist$数组,当我们需要查找 $1->5$的最短路径时,直接查$dist[1][5]$ @@ -102,7 +104,7 @@ void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } -int dist[6][N]; +int dis[6][N]; int id[6]; // 0号索引:佳佳的家,其它5个亲戚,分别下标为1~5,值为所在的车站编号 /* @@ -113,13 +115,13 @@ bool st[N]; /* S:出发车站编号 - dist[]:是全局变量dist[6][N]的某一个二维,其实是一个一维数组 + dis[]:是全局变量dis[6][N]的某一个二维,其实是一个一维数组 C++的特点:如果数组做参数传递的话,将直接修改原地址的数据 此数组传值方式可以让我们深入理解C++的二维数组本质:就是多个一维数组,给数组头就可以顺序找到其它相关数据 - 计算的结果:获取到S出发到其它各个站点的最短距离,记录到dist[S][站点号]中 + 计算的结果:获取到S出发到其它各个站点的最短距离,记录到dis[S][站点号]中 */ -void dijkstra(int S, int dist[]) { - dist[S] = 0; +void dijkstra(int S, int dis[]) { + dis[S] = 0; memset(st, false, sizeof st); priority_queue, greater> q; q.push({0, S}); @@ -132,9 +134,9 @@ void dijkstra(int S, int dist[]) { st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; - if (dist[v] > dist[u] + w[i]) { - dist[v] = dist[u] + w[i]; - q.push({dist[v], v}); + if (dis[v] > dis[u] + w[i]) { + dis[v] = dis[u] + w[i]; + q.push({dis[v], v}); } } } @@ -153,10 +155,10 @@ void dfs(int u, int pre, int sum) { for (int i = 1; i <= 5; i++) // 在当前位置上,枚举每个可能出现在亲戚站点 if (!st[i]) { // 如果这个亲戚没走过 st[i] = true; // 走它 - // 本位置填充完,下一个位置,需要传递前序是i,走过的路径和是sum+dist[pre][id[i]].因为提前打好表了,所以肯定是最小值,直接用就行了  + // 本位置填充完,下一个位置,需要传递前序是i,走过的路径和是sum+dis[pre][id[i]].因为提前打好表了,所以肯定是最小值,直接用就行了  // 需要注意的是一维是 6的上限,也就是 佳佳家+五个亲戚 ,而不是 车站号(佳佳家+五个亲戚) !因为这样的话,数据就很大,数组开起来麻烦,可能会MLE // 要注意学习使用小的数据标号进行事情描述的思想 - dfs(u + 1, i, sum + dist[pre][id[i]]); + dfs(u + 1, i, sum + dis[pre][id[i]]); st[i] = false; // 回溯 } } @@ -178,10 +180,10 @@ int main() { // 计算从某个亲戚所在的车站出发,到达其它几个点的最短路径 // 因为这样会产生多组最短距离,需要一个二维的数组进行存储 - memset(dist, 0x3f, sizeof dist); - for (int i = 0; i < 6; i++) dijkstra(id[i], dist[i]); + memset(dis, 0x3f, sizeof dis); + for (int i = 0; i < 6; i++) dijkstra(id[i], dis[i]); // 枚举每个亲戚所在的车站站点,多次Dijkstra,分别计算出以id[i]这个车站出发,到达其它点的最短距离,相当于打表 - // 将结果距离保存到给定的二维数组dist的第二维中去,第一维是指从哪个车站点出发的意思 + // 将结果距离保存到给定的二维数组dis的第二维中去,第一维是指从哪个车站点出发的意思 // dfs还要用这个st数组做其它用途,所以,需要再次的清空 memset(st, 0, sizeof st); From 80dcfe0412444a4973dbc261c3290aeba64cfa40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 29 Dec 2023 11:09:04 +0800 Subject: [PATCH 28/87] 'commit' --- .../AcWing_TiGao/T3/MiniPathYingYong/340.cpp | 48 ++++++++++-------- .../AcWing_TiGao/T3/MiniPathYingYong/340.md | 50 +++++++++++-------- 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.cpp b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.cpp index b095692..e359dfe 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.cpp +++ b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.cpp @@ -8,39 +8,44 @@ int idx, h[N], e[M], w[M], ne[M]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } -int n; //点数 -int m; //边数 -bool st[N]; //记录是不是在队列中 -int k; //不超过K条电缆,由电话公司免费提供升级服务 -int dist[N]; //记录最短距离 -// u指的是我们现在选最小花费 -bool check(int x) { +int n; // 点数 +int m; // 边数 + +int k; // 不超过K条电缆,由电话公司免费提供升级服务 + +bool st[N]; // 记录是不是在队列中 +int dis[N]; // 记录最短距离 + +// mid指的是我们现在选最小花费 +bool check(int mid) { + // 需要跑多次dijkstra,所以需要清空状态数组 memset(st, false, sizeof st); - memset(dist, 0x3f, sizeof dist); + memset(dis, 0x3f, sizeof dis); priority_queue, greater> q; - dist[1] = 0; + dis[1] = 0; q.push({0, 1}); while (q.size()) { PII t = q.top(); q.pop(); - int d = t.first, u = t.second; + int u = t.second; if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { - int j = e[i], v = w[i] > x; //如果有边比我们现在选的这条边大,那么这条边对方案的贡献为1,反之为0 - if (dist[j] > d + v) { - dist[j] = d + v; - q.push({dist[j], j}); + int j = e[i]; + int v = w[i] > mid; // 如果有边比我们现在选的这条边大,那么这条边对方案的贡献为1,反之为0 + if (dis[j] > dis[u] + v) { + dis[j] = dis[u] + v; + q.push({dis[j], j}); } } } - //如果按上面的方法计算后,n结点没有被松弛操作修改距离,则表示n不可达 - if (dist[n] == INF) { - puts("-1"); //不可达,直接输出-1 + // 如果按上面的方法计算后,n结点没有被松弛操作修改距离,则表示n不可达 + if (dis[n] == INF) { + puts("-1"); // 不可达,直接输出-1 exit(0); } - return dist[n] <= k; //如果有k+1条边比我们现在这条边大,那么这个升级方案就是不合法的,反之就合法 + return dis[n] <= k; // 如果有k+1条边比我们现在这条边大,那么这个升级方案就是不合法的,反之就合法 } int main() { memset(h, -1, sizeof h); @@ -50,11 +55,12 @@ int main() { cin >> a >> b >> c; add(a, b, c), add(b, a, c); } - /*这里二分的是直接面对答案设问:最少花费 + /* + 这里二分的是直接面对答案设问: 至少用多少钱 可以完成升级 依题意,最少花费其实是所有可能的路径中,第k+1条边的花费 如果某条路径不存在k+1条边(边数小于k+1),此时花费为0 - 同时,任意一条边的花费不会大于1e6 - 整理一下,这里二分枚举的值其实是0 ~ 1e6*/ + 同时,任意一条边的花费不会大于1e6,所以,这里二分枚举范围:0 ~ 1e6 + */ int l = 0, r = 1e6; while (l < r) { int mid = (l + r) >> 1; diff --git a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.md b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.md index 9685aed..e5ccee9 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.md +++ b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/340.md @@ -75,7 +75,7 @@ $0≤K using namespace std; @@ -87,39 +87,44 @@ int idx, h[N], e[M], w[M], ne[M]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } -int n; //点数 -int m; //边数 -bool st[N]; //记录是不是在队列中 -int k; //不超过K条电缆,由电话公司免费提供升级服务 -int dist[N]; //记录最短距离 -// u指的是我们现在选最小花费 -bool check(int x) { +int n; // 点数 +int m; // 边数 + +int k; // 不超过K条电缆,由电话公司免费提供升级服务 + +bool st[N]; // 记录是不是在队列中 +int dis[N]; // 记录最短距离 + +// mid指的是我们现在选最小花费 +bool check(int mid) { + // 需要跑多次dijkstra,所以需要清空状态数组 memset(st, false, sizeof st); - memset(dist, 0x3f, sizeof dist); + memset(dis, 0x3f, sizeof dis); priority_queue, greater> q; - dist[1] = 0; + dis[1] = 0; q.push({0, 1}); while (q.size()) { PII t = q.top(); q.pop(); - int d = t.first, u = t.second; + int u = t.second; if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { - int j = e[i], v = w[i] > x; //如果有边比我们现在选的这条边大,那么这条边对方案的贡献为1,反之为0 - if (dist[j] > d + v) { - dist[j] = d + v; - q.push({dist[j], j}); + int j = e[i]; + int v = w[i] > mid; // 如果有边比我们现在选的这条边大,那么这条边对方案的贡献为1,反之为0 + if (dis[j] > dis[u] + v) { + dis[j] = dis[u] + v; + q.push({dis[j], j}); } } } - //如果按上面的方法计算后,n结点没有被松弛操作修改距离,则表示n不可达 - if (dist[n] == INF) { - puts("-1"); //不可达,直接输出-1 + // 如果按上面的方法计算后,n结点没有被松弛操作修改距离,则表示n不可达 + if (dis[n] == INF) { + puts("-1"); // 不可达,直接输出-1 exit(0); } - return dist[n] <= k; //如果有k+1条边比我们现在这条边大,那么这个升级方案就是不合法的,反之就合法 + return dis[n] <= k; // 如果有k+1条边比我们现在这条边大,那么这个升级方案就是不合法的,反之就合法 } int main() { memset(h, -1, sizeof h); @@ -129,11 +134,12 @@ int main() { cin >> a >> b >> c; add(a, b, c), add(b, a, c); } - /*这里二分的是直接面对答案设问:最少花费 + /* + 这里二分的是直接面对答案设问: 至少用多少钱 可以完成升级 依题意,最少花费其实是所有可能的路径中,第k+1条边的花费 如果某条路径不存在k+1条边(边数小于k+1),此时花费为0 - 同时,任意一条边的花费不会大于1e6 - 整理一下,这里二分枚举的值其实是0 ~ 1e6*/ + 同时,任意一条边的花费不会大于1e6,所以,这里二分枚举范围:0 ~ 1e6 + */ int l = 0, r = 1e6; while (l < r) { int mid = (l + r) >> 1; From 6099843cb7179475bbc7f537e0e5188d32d35e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 29 Dec 2023 11:33:13 +0800 Subject: [PATCH 29/87] 'commit' --- .../AcWing_TiGao/T3/MiniPathYingYong/342.md | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md index e665252..8c52fcb 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md +++ b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md @@ -34,18 +34,18 @@ **输出格式** 第 $1..T$ 行:第 $i$ 行输出从 $S$ 到达城镇 $i$ 的最小花费,如果不存在,则输出 `NO PATH`。 -### 二、$Dijkstra$不能处理负权边,但可以处理负权初值 +### 二、$Dijkstra$不能处理负权边 我们说了$Dijkstra$算法不能解决带有负权边的图,这是为什么呢?下面用一个例子讲解一下 ![](https://img-blog.csdnimg.cn/51115e15bd3040d7a8b3980b523ed943.png) 以这里图为例,一共有五个点,也就说要循环$5$次,确定每个点的最短距离 用$Dijkstra$算法解决的的详细步骤 -> 1. 初始$dist[1] = 0$,$1$号点距离起点$1$的距离为$0$ -> 2. 找到了未标识且离起点$1$最近的结点$1$,标记$1$号点,用$1$号点更新和它相连点的距离,$2$号点被更新成$dist[2] = 2$,$3$号点被更新成$dist[3] = 5$ -> 3. 找到了未标识且离起点$1$最近的结点$2$,标识$2$号点,用$2$号点更新和它相连点的距离,$4$号点被更新成$dist[4] = 4$ -> 4. 找到了未标识且离起点$1$最近的结点$4$,标识$4$号点,用$4$号点更新和它相连点的距离,$5$号点被更新成$dist[5] = 5$ -> 5. 找到了未标识且离起点$1$最近的结点$3$,标识$3$号点,用$3$号点更新和它相连点的距离,$4$号点被更新成$dist[4] = 3$ +> 1. 初始$dis[1] = 0$,$1$号点距离起点$1$的距离为$0$ +> 2. 找到了未标识且离起点$1$最近的结点$1$,标记$1$号点,用$1$号点更新和它相连点的距离,$2$号点被更新成$dis[2] = 2$,$3$号点被更新成$dis[3] = 5$ +> 3. 找到了未标识且离起点$1$最近的结点$2$,标识$2$号点,用$2$号点更新和它相连点的距离,$4$号点被更新成$dis[4] = 4$ +> 4. 找到了未标识且离起点$1$最近的结点$4$,标识$4$号点,用$4$号点更新和它相连点的距离,$5$号点被更新成$dis[5] = 5$ +> 5. 找到了未标识且离起点$1$最近的结点$3$,标识$3$号点,用$3$号点更新和它相连点的距离,$4$号点被更新成$dis[4] = 3$ > **结果** @@ -56,10 +56,8 @@ > 我们可以发现如果有负权边的话$4$号点经过标记后还可以继续更新 但此时$4$号点已经被标记过了,所以$4$号点不能被更新了,只能一条路走到黑 当用负权边更新$4$号点后$5$号点距离起点的距离我们可以发现可以进一步缩小成$4$。 -所以总结下来就是:$dijkstra$**不能解决负权边** 是因为 $dijkstra$要求每个点被确定后,$dist[j]$就是最短距离了,之后就不能再被更新了(**一锤子买卖**),而如果有负权边的话,那已经确定的点的$dist[j]$不一定是最短了,可能还可以通过负权边进行更新。 +所以总结下来就是:$dijkstra$ **不能解决负权边** 是因为 $dijkstra$要求每个点被确定后,$dis[j]$就是最短距离了,之后就不能再被更新了(**一锤子买卖**),而如果有负权边的话,那已经确定的点的$dis[j]$不一定是最短了,可能还可以通过负权边进行更新。 -**负权初始值** -那如果不是负权的边长,而是负权的初值呢?这个就没关系了,因为初值不影响算法逻辑,不信你看下有好多算法题都是判断$INF/2$,正无穷不也是在过程中松弛操作更改过吗,你是负的初始值也是没有问题,可以正确运行算法。 ### 三、拓扑序+$Dijkstra$ + 缩点 @@ -99,7 +97,7 @@ int id[N]; // 节点在哪个连通块中 vector block[N]; // 连通块包含哪些节点 int bcnt; // 连通块序号计数器 -int dist[N]; // 最短距离(结果数组) +int dis[N]; // 最短距离(结果数组) int in[N]; // 每个DAG(节点即连通块)的入度 bool st[N]; // dijkstra用的是不是在队列中的数组 queue q; // 拓扑序用的队列 @@ -120,12 +118,12 @@ void dijkstra(int bid) { priority_queue, greater> pq; /* 因为不确定连通块内的哪个点可以作为起点,所以就一股脑全加进来就行了, - 反正很多点的dist都是inf(这些都是不能成为起点的),那么可以作为起点的就自然出现在堆顶了 + 反正很多点的dis都是inf(这些都是不能成为起点的),那么可以作为起点的就自然出现在堆顶了 因为上面的写法把拓扑排序和dijkstra算法拼在一起了,如果不把所有点都加入堆, 会导致后面其他块的din[]没有减去前驱边,从而某些块没有被拓扑排序遍历到。 */ - for (auto u : block[bid]) pq.push({dist[u], u}); + for (auto u : block[bid]) pq.push({dis[u], u}); while (pq.size()) { int u = pq.top().second; @@ -136,10 +134,10 @@ void dijkstra(int bid) { int v = e[i]; if (st[v]) continue; - if (dist[v] > dist[u] + w[i]) { - dist[v] = dist[u] + w[i]; + if (dis[v] > dis[u] + w[i]) { + dis[v] = dis[u] + w[i]; // 如果是同团中的道路,需要再次进入Dijkstra的小顶堆,以便计算完整个团中的路径最小值 - if (id[u] == id[v]) pq.push({dist[v], v}); + if (id[u] == id[v]) pq.push({dis[v], v}); } /*如果u和v不在同一个团中,说明遍历到的是航线 此时,需要与拓扑序算法结合,尝试剪掉此边,是不是可以形成入度为的团 @@ -171,8 +169,8 @@ int main() { memset(h, -1, sizeof h); // 初始化 scanf("%d %d %d %d", &T, &R, &P, &S); // 城镇数量,道路数量,航线数量,出发点 - memset(dist, 0x3f, sizeof dist); // 初始化最短距离 - dist[S] = 0; // 出发点距离自己的长度是0,其它的最短距离目前是INF + memset(dis, 0x3f, sizeof dis); // 初始化最短距离 + dis[S] = 0; // 出发点距离自己的长度是0,其它的最短距离目前是INF int a, b, c; // 起点,终点,权值 @@ -211,10 +209,10 @@ int main() { // 从S到达城镇i的最小花费 for (int i = 1; i <= T; i++) { - if (dist[i] > INF / 2) + if (dis[i] > INF / 2) puts("NO PATH"); else - cout << dist[i] << endl; + cout << dis[i] << endl; } return 0; } From 56e04c9ce638e8a7aca4e88ab991fbe50b951841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 29 Dec 2023 13:12:13 +0800 Subject: [PATCH 30/87] 'commit' --- .../AcWing_TiGao/T3/MiniPathYingYong/342.cpp | 30 +++++++++---------- .../AcWing_TiGao/T3/MiniPathYingYong/342.md | 14 ++++----- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.cpp b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.cpp index 917fec2..ad895fa 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.cpp +++ b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.cpp @@ -20,7 +20,7 @@ int id[N]; // 节点在哪个连通块中 vector block[N]; // 连通块包含哪些节点 int bcnt; // 连通块序号计数器 -int dist[N]; // 最短距离(结果数组) +int dis[N]; // 最短距离(结果数组) int in[N]; // 每个DAG(节点即连通块)的入度 bool st[N]; // dijkstra用的是不是在队列中的数组 queue q; // 拓扑序用的队列 @@ -31,8 +31,8 @@ void dfs(int u, int bid) { block[bid].push_back(u); // ② 记录bid团包含u节点 // 枚举u节点的每一条出边,将对端的城镇也加入到bid这个团中 for (int i = h[u]; ~i; i = ne[i]) { - int v = e[i]; - if (!id[v]) dfs(v, bid); // Flood Fill + int j = e[i]; + if (!id[j]) dfs(j, bid); // Flood Fill } } @@ -41,12 +41,12 @@ void dijkstra(int bid) { priority_queue, greater> pq; /* 因为不确定连通块内的哪个点可以作为起点,所以就一股脑全加进来就行了, - 反正很多点的dist都是inf(这些都是不能成为起点的),那么可以作为起点的就自然出现在堆顶了 + 反正很多点的dis都是inf(这些都是不能成为起点的),那么可以作为起点的就自然出现在堆顶了 因为上面的写法把拓扑排序和dijkstra算法拼在一起了,如果不把所有点都加入堆, 会导致后面其他块的din[]没有减去前驱边,从而某些块没有被拓扑排序遍历到。 */ - for (auto u : block[bid]) pq.push({dist[u], u}); + for (auto u : block[bid]) pq.push({dis[u], u}); while (pq.size()) { int u = pq.top().second; @@ -57,10 +57,10 @@ void dijkstra(int bid) { int v = e[i]; if (st[v]) continue; - if (dist[v] > dist[u] + w[i]) { - dist[v] = dist[u] + w[i]; + if (dis[v] > dis[u] + w[i]) { + dis[v] = dis[u] + w[i]; // 如果是同团中的道路,需要再次进入Dijkstra的小顶堆,以便计算完整个团中的路径最小值 - if (id[u] == id[v]) pq.push({dist[v], v}); + if (id[u] == id[v]) pq.push({dis[v], v}); } /*如果u和v不在同一个团中,说明遍历到的是航线 此时,需要与拓扑序算法结合,尝试剪掉此边,是不是可以形成入度为的团 @@ -92,12 +92,12 @@ int main() { memset(h, -1, sizeof h); // 初始化 scanf("%d %d %d %d", &T, &R, &P, &S); // 城镇数量,道路数量,航线数量,出发点 - memset(dist, 0x3f, sizeof dist); // 初始化最短距离 - dist[S] = 0; // 出发点距离自己的长度是0,其它的最短距离目前是INF + memset(dis, 0x3f, sizeof dis); // 初始化最短距离 + dis[S] = 0; // 出发点距离自己的长度是0,其它的最短距离目前是INF int a, b, c; // 起点,终点,权值 - while (R--) { // 读入道路 + while (R--) { // 读入道路,团内无向图 scanf("%d %d %d", &a, &b, &c); add(a, b, c), add(b, a, c); // 连通块内是无向图 } @@ -124,18 +124,18 @@ int main() { while (P--) { scanf("%d %d %d", &a, &b, &c); add(a, b, c); // 单向边 - in[id[b]]++; // b节点所在团入度+1 + in[id[b]]++; // b节点所在团的番号,也就是某个团的入度+1 } - // 拓扑序 + // 拓扑 topsort(); // 从S到达城镇i的最小花费 for (int i = 1; i <= T; i++) { - if (dist[i] > INF / 2) + if (dis[i] > INF / 2) puts("NO PATH"); else - cout << dist[i] << endl; + cout << dis[i] << endl; } return 0; } \ No newline at end of file diff --git a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md index 8c52fcb..1e64079 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md +++ b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md @@ -97,7 +97,7 @@ int id[N]; // 节点在哪个连通块中 vector block[N]; // 连通块包含哪些节点 int bcnt; // 连通块序号计数器 -int dis[N]; // 最短距离(结果数组) +int dis[N]; // 最短距离(结果数组) int in[N]; // 每个DAG(节点即连通块)的入度 bool st[N]; // dijkstra用的是不是在队列中的数组 queue q; // 拓扑序用的队列 @@ -108,8 +108,8 @@ void dfs(int u, int bid) { block[bid].push_back(u); // ② 记录bid团包含u节点 // 枚举u节点的每一条出边,将对端的城镇也加入到bid这个团中 for (int i = h[u]; ~i; i = ne[i]) { - int v = e[i]; - if (!id[v]) dfs(v, bid); // Flood Fill + int j = e[i]; + if (!id[j]) dfs(j, bid); // Flood Fill } } @@ -170,11 +170,11 @@ int main() { scanf("%d %d %d %d", &T, &R, &P, &S); // 城镇数量,道路数量,航线数量,出发点 memset(dis, 0x3f, sizeof dis); // 初始化最短距离 - dis[S] = 0; // 出发点距离自己的长度是0,其它的最短距离目前是INF + dis[S] = 0; // 出发点距离自己的长度是0,其它的最短距离目前是INF int a, b, c; // 起点,终点,权值 - while (R--) { // 读入道路 + while (R--) { // 读入道路,团内无向图 scanf("%d %d %d", &a, &b, &c); add(a, b, c), add(b, a, c); // 连通块内是无向图 } @@ -201,10 +201,10 @@ int main() { while (P--) { scanf("%d %d %d", &a, &b, &c); add(a, b, c); // 单向边 - in[id[b]]++; // b节点所在团入度+1 + in[id[b]]++; // b节点所在团的番号,也就是某个团的入度+1 } - // 拓扑序 + // 拓扑 topsort(); // 从S到达城镇i的最小花费 From 38924c3ff62071d5710554ac91e15f8c8901db21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 29 Dec 2023 13:26:05 +0800 Subject: [PATCH 31/87] 'commit' --- TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md index 1e64079..a8ba284 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md +++ b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/342.md @@ -72,6 +72,8 @@ #### 算法步骤
+扩展:用拓扑排序解决dag带负权图的最短路问题 +![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202312291324775.png) #### $Code$ ```cpp {.line-numbers} From 22997b64f5e2e8d03e9127dfb654cda197450ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 29 Dec 2023 14:13:00 +0800 Subject: [PATCH 32/87] 'commit' --- .../AcWing_TiGao/T3/MiniPathYingYong/341.cpp | 30 ++++---- .../AcWing_TiGao/T3/MiniPathYingYong/341.md | 70 ++++++++++--------- 2 files changed, 53 insertions(+), 47 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/341.cpp b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/341.cpp index e94f362..00d9819 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/341.cpp +++ b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/341.cpp @@ -5,7 +5,7 @@ const int INF = 0x3f3f3f3f; const int N = 100010, M = 2000010; int n, m; -int dist1[N], dist2[N]; +int dis1[N], dis2[N]; // 正反建图,传入头数组指针 int h1[N], h2[N], e[M], ne[M], w[M], idx; @@ -17,10 +17,10 @@ void add(int *h, int a, int b, int c = 0) { int v[N]; void dijkstra1() { - memset(dist1, 0x3f, sizeof dist1); + memset(dis1, 0x3f, sizeof dis1); priority_queue, greater> q; - dist1[1] = v[1]; - q.push({dist1[1], 1}); + dis1[1] = v[1]; + q.push({dis1[1], 1}); while (q.size()) { int u = q.top().second; @@ -28,28 +28,29 @@ void dijkstra1() { for (int i = h1[u]; ~i; i = ne[i]) { int j = e[i]; - if (dist1[j] > min(dist1[u], v[j])) { - dist1[j] = min(dist1[u], v[j]); - q.push({dist1[j], j}); + if (dis1[j] > min(dis1[u], v[j])) { + dis1[j] = min(dis1[u], v[j]); + q.push({dis1[j], j}); } } } } void dijkstra2() { - memset(dist2, -0x3f, sizeof dist2); + memset(dis2, -0x3f, sizeof dis2); priority_queue q; - dist2[n] = v[n]; - q.push({dist2[n], n}); + dis2[n] = v[n]; + q.push({dis2[n], n}); while (q.size()) { int u = q.top().second; q.pop(); + for (int i = h2[u]; ~i; i = ne[i]) { int j = e[i]; - if (dist2[j] < max(dist2[u], v[j])) { - dist2[j] = max(dist2[u], v[j]); - q.push({dist2[j], j}); + if (dis2[j] < max(dis2[u], v[j])) { + dis2[j] = max(dis2[u], v[j]); + q.push({dis2[j], j}); } } } @@ -86,12 +87,13 @@ int main() { } // 正向图跑一遍dijkstra dijkstra1(); + // 反向图跑一遍dijkstra dijkstra2(); int ans = 0; for (int i = 1; i <= n; i++) - ans = max(dist2[i] - dist1[i], ans); + ans = max(dis2[i] - dis1[i], ans); printf("%d\n", ans); return 0; diff --git a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/341.md b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/341.md index 5c30e11..8279dad 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathYingYong/341.md +++ b/TangDou/AcWing_TiGao/T3/MiniPathYingYong/341.md @@ -66,38 +66,39 @@ $1≤$各城市水晶球价格$≤100$ **阿龙决定从 $1$ 号城市出发,并最终在 $n$ 号城市结束自己的旅行。** -卖完后要回到点$n$,然而,题目并没有保证所有点都能去到点$n$,而且,**不是所有边都是无向边**。 +终点是$n$,但题目并没有保证所有点都能去到点$n$。 要知道哪些点不能去到点$n$,可以 **反向建图**,在这张图以$n$为起点看能到达哪些点。 -分析: 这道题需要建两个图,一个为 **正向图** ,一个为 **反向图** ,考虑分别跑最短路变形得到$dist1$数组和$dist2$数组: -* $dist1[i]$表示从点$1$到点$i$的所有路径上经过的 **最小点权** -* $dist2[i]$表示从点$n$经过反向边到点$i$的所有路径上经过的 **最大点权**。 +**分析**: 这道题需要建两个图,一个为 **正向图** ,一个为 **反向图** ,考虑分别跑$Dijkstra$算法得到$dis1$数组和$dis2$数组: + +* $dis1[i]$:从点$1$到点$i$的所有路径上经过的 **最小点权** +* $dis2[i]$:从点$n$经过反向边到点$i$的所有路径上经过的 **最大点权** 当求出这两个数组后就可以枚举路径上的 **中间点**$i$,最终答案就是 -$$\large max(dist2[i]-dis1t[i])$$ +$$\large max(dis2[i]-dis1[i])$$ -考虑如何通过最短路求出$dist$数组,常规思路就是 **最短路变形**,把松弛条件改为: -```cpp {.line-numbers} -if(dist1[j] > min(dist1[u], v[j])){ - dist1[j] = min(dist1[u], v[j]); - q.push({dist1[j], j}); -} -``` -**理论** 上这就没问题了,不过这道题目比较特殊,由于图中 **可能出现回路**,且$dist$值是记录 **点权的最值** ,在某些情况下是 **具有后效性**的,如下图: +**理论** 上这就没问题了,不过这道题目比较特殊,由于图中 **可能出现回路**,且$dis$值是记录 **点权的最值** ,在某些情况下是 **具有后效性**的,如下图:
-**点权** 用绿色数字标示在点号下方,可以发现在点$2$处会经过一个回路再次回到点$2$,但在这之前点$5$的$dist$已经被更新为$3$了,之后回到点$2$,由于$st[2] == true$直接$continue$,虽然此时$dist[2] == 1$但却无法把$1$传递给点$5$了。 +**点权** 用绿色数字标示在点号下方,可以发现在点$2$处会经过一个回路再次回到点$2$,但在这之前点$5$的$dis$已经被更新为$3$了 +>解释:因为$1 \rightarrow 2 \rightarrow 5$这条路线上,在点$2$时,水晶球的价格最便宜,价格是$3$ -**解决方法**:$dijkstra$算法中去掉$st$的限制,让整个算法不断迭代,直到无法更新导致队空退出循环。 +之后回到点$2$,由于$st[2] == true$直接$continue$,虽然此时$dis[2] == 1$但却无法把$1$传递给点$5$了。 + +采用办法 +在$dijkstra$算法中去掉$st$的限制,让整个算法不断迭代,直到无法更新导致队空退出循环。这就类似于$DP$的所有情况尝试,不断刷新最新最小价格! **总结** -本题用$Dijkstra$的话,其实已经不是传统意义上的$Dijkstra$了,因为它允许出边再进入队列!(去掉了$st$数组 ,因为有环嘛),指望 **更无可更,无需再更**。这么用$Dijkstra$其实就不如用$SPFA$来的直接了,$SPFA$本身就是更无可更,无需再更。 +本题用$Dijkstra$的话,其实已经不是传统意义上的$Dijkstra$了,因为它允许出边再进入队列!(去掉了$st$数组 ,因为有环嘛),指望 **更无可更,无需再更**。 + +**最大最小值**,其实也不是传统最短、最长路的路径累加和,而是类似于$DP$的思路,一路走来一路维护到达当前点的最大点权和最小点权。 -**最大最小值**,其实也不是传统最短、最长路的路径累加和,而是类似于$DP$的思路,一路走来一路维护到达当前点的最大点权和最小点权。严格意义上来讲,采用的$Dijkstra$或$SPFA$都不是本身的含义,只是一个协助$DP$的枚举过程。 +**配合$DP$** +严格意义上来讲,采用的$Dijkstra$不是本身的含义,只是一个协助$DP$的枚举过程。 #### $Code$ ```cpp {.line-numbers} @@ -108,21 +109,22 @@ const int INF = 0x3f3f3f3f; const int N = 100010, M = 2000010; int n, m; -int dist1[N], dist2[N]; +int dis1[N], dis2[N]; // 正反建图,传入头数组指针 int h1[N], h2[N], e[M], ne[M], w[M], idx; -void add(int *hh, int a, int b, int c = 0) { - e[idx] = b, ne[idx] = hh[a], w[idx] = c, hh[a] = idx++; +void add(int *h, int a, int b, int c = 0) { + e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } + // 每个节点的价值 int v[N]; void dijkstra1() { - memset(dist1, 0x3f, sizeof dist1); + memset(dis1, 0x3f, sizeof dis1); priority_queue, greater> q; - dist1[1] = v[1]; - q.push({dist1[1], 1}); + dis1[1] = v[1]; + q.push({dis1[1], 1}); while (q.size()) { int u = q.top().second; @@ -130,28 +132,29 @@ void dijkstra1() { for (int i = h1[u]; ~i; i = ne[i]) { int j = e[i]; - if (dist1[j] > min(dist1[u], v[j])) { - dist1[j] = min(dist1[u], v[j]); - q.push({dist1[j], j}); + if (dis1[j] > min(dis1[u], v[j])) { + dis1[j] = min(dis1[u], v[j]); + q.push({dis1[j], j}); } } } } void dijkstra2() { - memset(dist2, -0x3f, sizeof dist2); + memset(dis2, -0x3f, sizeof dis2); priority_queue q; - dist2[n] = v[n]; - q.push({dist2[n], n}); + dis2[n] = v[n]; + q.push({dis2[n], n}); while (q.size()) { int u = q.top().second; q.pop(); + for (int i = h2[u]; ~i; i = ne[i]) { int j = e[i]; - if (dist2[j] < max(dist2[u], v[j])) { - dist2[j] = max(dist2[u], v[j]); - q.push({dist2[j], j}); + if (dis2[j] < max(dis2[u], v[j])) { + dis2[j] = max(dis2[u], v[j]); + q.push({dis2[j], j}); } } } @@ -188,12 +191,13 @@ int main() { } // 正向图跑一遍dijkstra dijkstra1(); + // 反向图跑一遍dijkstra dijkstra2(); int ans = 0; for (int i = 1; i <= n; i++) - ans = max(dist2[i] - dist1[i], ans); + ans = max(dis2[i] - dis1[i], ans); printf("%d\n", ans); return 0; From 917c198eac93e4e2aa90b68f29294d7bad0b031e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 2 Jan 2024 08:06:57 +0800 Subject: [PATCH 33/87] 'commit' --- .../T3/MiniPathExtend}/1131.cpp | 0 .../T3/MiniPathExtend}/1131.md | 0 .../T3/MiniPathExtend}/1134.drawio | 0 .../T3/MiniPathExtend}/1134.md | 0 .../T3/MiniPathExtend}/1134_Bfs.cpp | 0 .../T3/MiniPathExtend}/1134_Dijkstra.cpp | 0 .../T3/MiniPathExtend}/1137.md | 50 +++++++++---------- .../T3/MiniPathExtend}/1137_1.cpp | 30 +++++------ .../T3/MiniPathExtend}/1137_2.cpp | 26 +++++----- .../T3/MiniPathExtend}/383.cpp | 0 .../T3/MiniPathExtend}/383.md | 0 11 files changed, 53 insertions(+), 53 deletions(-) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathExtend}/1131.cpp (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathExtend}/1131.md (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathExtend}/1134.drawio (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathExtend}/1134.md (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathExtend}/1134_Bfs.cpp (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathExtend}/1134_Dijkstra.cpp (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathExtend}/1137.md (79%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathExtend}/1137_1.cpp (57%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathExtend}/1137_2.cpp (69%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathExtend}/383.cpp (100%) rename TangDou/{AcWing/MiniPath => AcWing_TiGao/T3/MiniPathExtend}/383.md (100%) diff --git a/TangDou/AcWing/MiniPath/1131.cpp b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.cpp similarity index 100% rename from TangDou/AcWing/MiniPath/1131.cpp rename to TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.cpp diff --git a/TangDou/AcWing/MiniPath/1131.md b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.md similarity index 100% rename from TangDou/AcWing/MiniPath/1131.md rename to TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.md diff --git a/TangDou/AcWing/MiniPath/1134.drawio b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134.drawio similarity index 100% rename from TangDou/AcWing/MiniPath/1134.drawio rename to TangDou/AcWing_TiGao/T3/MiniPathExtend/1134.drawio diff --git a/TangDou/AcWing/MiniPath/1134.md b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134.md similarity index 100% rename from TangDou/AcWing/MiniPath/1134.md rename to TangDou/AcWing_TiGao/T3/MiniPathExtend/1134.md diff --git a/TangDou/AcWing/MiniPath/1134_Bfs.cpp b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134_Bfs.cpp similarity index 100% rename from TangDou/AcWing/MiniPath/1134_Bfs.cpp rename to TangDou/AcWing_TiGao/T3/MiniPathExtend/1134_Bfs.cpp diff --git a/TangDou/AcWing/MiniPath/1134_Dijkstra.cpp b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134_Dijkstra.cpp similarity index 100% rename from TangDou/AcWing/MiniPath/1134_Dijkstra.cpp rename to TangDou/AcWing_TiGao/T3/MiniPathExtend/1134_Dijkstra.cpp diff --git a/TangDou/AcWing/MiniPath/1137.md b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1137.md similarity index 79% rename from TangDou/AcWing/MiniPath/1137.md rename to TangDou/AcWing_TiGao/T3/MiniPathExtend/1137.md index cbfb451..d800308 100644 --- a/TangDou/AcWing/MiniPath/1137.md +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1137.md @@ -87,32 +87,32 @@ int h[N], e[M], w[M], ne[M], idx; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } -int d[N]; // 最短距离数组 +int dis[N]; // 最短距离数组 bool st[N]; // 是否进过队列 // 迪杰斯特拉 void dijkstra() { - memset(d, 0x3f, sizeof d); // 初始化大 - memset(st, 0, sizeof st); // 初始化为未出队列过 - priority_queue, greater> pq; // 小顶堆 - pq.push({0, 0}); // 出发点入队列 - d[0] = 0; // 出发点距离0 - - while (pq.size()) { - auto t = pq.top(); - pq.pop(); + memset(dis, 0x3f, sizeof dis); // 初始化大 + memset(st, 0, sizeof st); // 初始化为未出队列过 + priority_queue, greater> q; // 小顶堆 + q.push({0, 0}); // 出发点入队列 + dis[0] = 0; // 出发点距离0 + + while (q.size()) { + auto t = q.top(); + q.pop(); int u = t.second; if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; - if (d[j] > d[u] + w[i]) { - d[j] = d[u] + w[i]; - pq.push({d[j], j}); + if (dis[j] > dis[u] + w[i]) { + dis[j] = dis[u] + w[i]; + q.push({dis[j], j}); } } } // 注意:此处的S是终点,不是起点,不是起点,不是起点! - printf("%d\n", d[S] == INF ? -1 : d[S]); + printf("%d\n", dis[S] == INF ? -1 : dis[S]); } int main() { while (cin >> n >> m >> S) { @@ -126,11 +126,11 @@ int main() { add(a, b, c); } int T; - scanf("%d", &T); + cin >> T; while (T--) { int x; cin >> x; - add(0, x, 0); + add(0, x, 0); // 超级源点法 } dijkstra(); } @@ -153,25 +153,25 @@ void add(int a, int b, int c) { } int n, m; // n个点,m条边 int S; // 出发点 -int d[N]; // 距离数组 +int dis[N]; // 距离数组 bool st[N]; // Dijkstra是不是入过队列 void dijkstra() { priority_queue, greater> q; q.push({0, S}); - d[S] = 0; + dis[S] = 0; while (q.size()) { auto t = q.top(); - int u = t.second, dist = t.first; + int u = t.second; q.pop(); if (st[u]) continue; st[u] = true; - + for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; - if (d[j] > dist + w[i]) { - d[j] = dist + w[i]; - q.push({d[j], j}); + if (dis[j] > dis[u] + w[i]) { + dis[j] = dis[u] + w[i]; + q.push({dis[j], j}); } } } @@ -181,7 +181,7 @@ int main() { // 初始化 memset(st, 0, sizeof st); memset(h, -1, sizeof h); - memset(d, 0x3f, sizeof d); + memset(dis, 0x3f, sizeof dis); idx = 0; int ans = INF; @@ -197,7 +197,7 @@ int main() { cin >> T; while (T--) { cin >> x; - ans = min(ans, d[x]); + ans = min(ans, dis[x]); } printf("%d\n", ans == INF ? -1 : ans); } diff --git a/TangDou/AcWing/MiniPath/1137_1.cpp b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1137_1.cpp similarity index 57% rename from TangDou/AcWing/MiniPath/1137_1.cpp rename to TangDou/AcWing_TiGao/T3/MiniPathExtend/1137_1.cpp index 766140e..d6e0524 100644 --- a/TangDou/AcWing/MiniPath/1137_1.cpp +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1137_1.cpp @@ -11,32 +11,32 @@ int h[N], e[M], w[M], ne[M], idx; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } -int d[N]; // 最短距离数组 +int dis[N]; // 最短距离数组 bool st[N]; // 是否进过队列 // 迪杰斯特拉 void dijkstra() { - memset(d, 0x3f, sizeof d); // 初始化大 - memset(st, 0, sizeof st); // 初始化为未出队列过 - priority_queue, greater> pq; // 小顶堆 - pq.push({0, 0}); // 出发点入队列 - d[0] = 0; // 出发点距离0 + memset(dis, 0x3f, sizeof dis); // 初始化大 + memset(st, 0, sizeof st); // 初始化为未出队列过 + priority_queue, greater> q; // 小顶堆 + q.push({0, 0}); // 出发点入队列 + dis[0] = 0; // 出发点距离0 - while (pq.size()) { - auto t = pq.top(); - pq.pop(); + while (q.size()) { + auto t = q.top(); + q.pop(); int u = t.second; if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; - if (d[j] > d[u] + w[i]) { - d[j] = d[u] + w[i]; - pq.push({d[j], j}); + if (dis[j] > dis[u] + w[i]) { + dis[j] = dis[u] + w[i]; + q.push({dis[j], j}); } } } // 注意:此处的S是终点,不是起点,不是起点,不是起点! - printf("%d\n", d[S] == INF ? -1 : d[S]); + printf("%d\n", dis[S] == INF ? -1 : dis[S]); } int main() { while (cin >> n >> m >> S) { @@ -50,11 +50,11 @@ int main() { add(a, b, c); } int T; - scanf("%d", &T); + cin >> T; while (T--) { int x; cin >> x; - add(0, x, 0); + add(0, x, 0); // 超级源点法 } dijkstra(); } diff --git a/TangDou/AcWing/MiniPath/1137_2.cpp b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1137_2.cpp similarity index 69% rename from TangDou/AcWing/MiniPath/1137_2.cpp rename to TangDou/AcWing_TiGao/T3/MiniPathExtend/1137_2.cpp index 713b360..28a5a16 100644 --- a/TangDou/AcWing/MiniPath/1137_2.cpp +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1137_2.cpp @@ -10,25 +10,25 @@ void add(int a, int b, int c) { } int n, m; // n个点,m条边 int S; // 出发点 -int d[N]; // 距离数组 +int dis[N]; // 距离数组 bool st[N]; // Dijkstra是不是入过队列 void dijkstra() { - priority_queue, greater> pq; - pq.push({0, S}); - d[S] = 0; - while (pq.size()) { - auto t = pq.top(); - int u = t.second, dist = t.first; - pq.pop(); + priority_queue, greater> q; + q.push({0, S}); + dis[S] = 0; + while (q.size()) { + auto t = q.top(); + int u = t.second; + q.pop(); if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; - if (d[j] > dist + w[i]) { - d[j] = dist + w[i]; - pq.push({d[j], j}); + if (dis[j] > dis[u] + w[i]) { + dis[j] = dis[u] + w[i]; + q.push({dis[j], j}); } } } @@ -38,7 +38,7 @@ int main() { // 初始化 memset(st, 0, sizeof st); memset(h, -1, sizeof h); - memset(d, 0x3f, sizeof d); + memset(dis, 0x3f, sizeof dis); idx = 0; int ans = INF; @@ -54,7 +54,7 @@ int main() { cin >> T; while (T--) { cin >> x; - ans = min(ans, d[x]); + ans = min(ans, dis[x]); } printf("%d\n", ans == INF ? -1 : ans); } diff --git a/TangDou/AcWing/MiniPath/383.cpp b/TangDou/AcWing_TiGao/T3/MiniPathExtend/383.cpp similarity index 100% rename from TangDou/AcWing/MiniPath/383.cpp rename to TangDou/AcWing_TiGao/T3/MiniPathExtend/383.cpp diff --git a/TangDou/AcWing/MiniPath/383.md b/TangDou/AcWing_TiGao/T3/MiniPathExtend/383.md similarity index 100% rename from TangDou/AcWing/MiniPath/383.md rename to TangDou/AcWing_TiGao/T3/MiniPathExtend/383.md From c0d8b485d0ca33c5e862deb03c5c3023d87ea48e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 2 Jan 2024 08:31:14 +0800 Subject: [PATCH 34/87] 'commit' --- .../AcWing_TiGao/T3/MiniPathExtend/1131.cpp | 46 ++++++++-------- .../AcWing_TiGao/T3/MiniPathExtend/1131.md | 53 ++++++++++--------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.cpp b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.cpp index a0551f5..8c5ef11 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.cpp +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.cpp @@ -6,7 +6,7 @@ typedef pair PII; int g[N][N]; // 两个位置之间的间隔是什么,可能是某种门,或者是墙 int key[N]; // 某个坐标位置上有哪些钥匙,这是用数位压缩记录的,方便位运算 -int dist[N][1 << M]; // 哪个位置,在携带不同的钥匙情况下的状态 +int dis[N][1 << M]; // 哪个位置,在携带不同的钥匙情况下的状态 int n, m; // n行m列 int k; // 迷宫中门和墙的总数 int p; // p类钥匙 @@ -19,22 +19,23 @@ int get(int x, int y) { } int bfs() { - memset(dist, 0x3f, sizeof dist); // 初始化距离 - queue q; // bfs用的队列 + memset(dis, 0x3f, sizeof dis); // 初始化距离 + queue q; // bfs用的队列 - int t = get(1, 1); // 从编号1出发 - q.push({t, key[t]}); // 位置+携带钥匙的压缩状态 = 现在的真正状态 - dist[t][key[t]] = 0; // 初始状态的距离为0 + int S = get(1, 1); // 从编号1出发 + q.push({S, key[S]}); // 位置+携带钥匙的压缩状态 = 现在的真正状态 + dis[S][key[S]] = 0; // 初始状态的需要走的步数为0 while (q.size()) { PII x = q.front(); q.pop(); int u = x.first; // 出发点编号 - int st = x.second; // 钥匙状态 + int st = x.second; // 钥匙状态,为状态压缩的数字 - // 找到大兵瑞恩就结束了 - if (u == n * m) return dist[u][st]; + // dis[u][st]:到达了n*m,并且,当前状态是st: 找到大兵瑞恩就结束了,不用管最终的钥匙状态是什么 + // 是什么都是符合拯救大兵的目标的 + if (u == n * m) return dis[u][st]; // 四个方向 for (int i = 0; i < 4; i++) { @@ -42,27 +43,26 @@ int bfs() { int tx = (u - 1) / m + 1 + dx[i]; // 下一个位置 int ty = (u - 1) % m + 1 + dy[i]; - int tz = get(tx, ty); // 要去的坐标位置tz - int ts = st; // 复制出z结点携带过来的钥匙状态 + int T = get(tx, ty); // 要去的坐标位置T /* - g[z][tz] == 0 有墙,不能走 - g[z][tz] > 0 有门,有钥匙能走,无钥匙不能走 - g[z][tz] == -1 随便走 + g[z][T] == 0 有墙,不能走 + g[z][T] > 0 有门,有钥匙能走,无钥匙不能走 + g[z][T] == -1 随便走 */ - // 出界或有墙 - if (tx == 0 || ty == 0 || tx > n || ty > m || g[u][tz] == 0) continue; + // 出界或有墙,没有办法转移 + if (tx == 0 || ty == 0 || tx > n || ty > m || g[u][T] == 0) continue; - // 有门,并且, v这个状态中没有当前类型的钥匙 - if (g[u][tz] > 0 && !(st >> g[u][tz] & 1)) continue; + // 有门,并且, st这个状态中没有带过来当前类型的钥匙 + if (g[u][T] > 0 && !(st >> g[u][T] & 1)) continue; - // 捡起钥匙 - ts |= key[tz]; + // 捡起钥匙不会增加成本,所以,无条件捡起来钥匙 + int ST = st | key[T]; // 如果这个状态没有走过 - if (dist[tz][ts] == INF) { - q.push({tz, ts}); // 入队列 - dist[tz][ts] = dist[u][st] + 1; // 步数加1 + if (dis[T][ST] == INF) { + q.push({T, ST}); // 入队列 + dis[T][ST] = dis[u][st] + 1; // 步数加1 } } } diff --git a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.md b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.md index 7e30689..8654c54 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.md +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1131.md @@ -77,12 +77,12 @@ $1≤k≤150$ ### 二、解题思路 -试想下如果本题 **没有钥匙和门** 的条件,只要求从 **左上角** 走到 **右下角** 的最小步数,就是简单的迷宫问题了,可以使用$BFS$解决。 +试想下如果本题 **没有钥匙和门** 的条件,只要求从 **左上角** 走到 **右下角** 的最小步数,就是简单的迷宫问题了,可以使用$bfs$解决。 #### 状态表示 -加上钥匙和门的的条件,便是**类似于八数码问题**了。实际上$BFS$解决的最短路问题都可以看作**求从初始状态到结束状态需要的最小转移次数**: +加上钥匙和门的的条件,便是**类似于八数码问题**了。实际上$bfs$解决的最短路问题都可以看作 **求从初始状态到结束状态需要的最小转移次数**: -普通迷宫问题的 **状态** 就是 **当前所在的坐标**,八数码问题的 **状态** 就是**当前棋盘的局面**。 +普通迷宫问题的 **状态** 就是 **当前所在的坐标**,八数码问题的 **状态** 就是 **当前棋盘的局面**。 本题在迷宫问题上加上了 **钥匙和门** 的条件,显然,处在同一个坐标下,**持有钥匙和不持有钥匙就不是同一个状态了**,为了能够清楚的表示每个状态,除了当前坐标外还需要加上当前获得的钥匙信息,即$f[x][y][st]$表示当前处在$(x,y)$位置下持有钥匙状态为$st$,将二维坐标压缩成一维就得到$f[z][st]$这样的状态表示了,或者说,$z$是格子的编号,从上到下,从左而右的编号依次为$1$到$n*m$,$st$为$0110$时,表示持有第$1,2$类钥匙,这里注意我在 表示状态时抛弃了最右边的一位,因为钥匙编号从$1$开始,我想确定是否持有第$i$类钥匙时,只需要判断`st >> i & 1`是不是等于$1$即可。 @@ -108,7 +108,7 @@ typedef pair PII; int g[N][N]; // 两个位置之间的间隔是什么,可能是某种门,或者是墙 int key[N]; // 某个坐标位置上有哪些钥匙,这是用数位压缩记录的,方便位运算 -int dist[N][1 << M]; // 哪个位置,在携带不同的钥匙情况下的状态 +int dis[N][1 << M]; // 哪个位置,在携带不同的钥匙情况下的状态 int n, m; // n行m列 int k; // 迷宫中门和墙的总数 int p; // p类钥匙 @@ -121,22 +121,23 @@ int get(int x, int y) { } int bfs() { - memset(dist, 0x3f, sizeof dist); // 初始化距离 - queue q; // bfs用的队列 + memset(dis, 0x3f, sizeof dis); // 初始化距离 + queue q; // bfs用的队列 - int t = get(1, 1); // 从编号1出发 - q.push({t, key[t]}); // 位置+携带钥匙的压缩状态 = 现在的真正状态 - dist[t][key[t]] = 0; // 初始状态的距离为0 + int S = get(1, 1); // 从编号1出发 + q.push({S, key[S]}); // 位置+携带钥匙的压缩状态 = 现在的真正状态 + dis[S][key[S]] = 0; // 初始状态的需要走的步数为0 while (q.size()) { PII x = q.front(); q.pop(); int u = x.first; // 出发点编号 - int st = x.second; // 钥匙状态 + int st = x.second; // 钥匙状态,为状态压缩的数字 - // 找到大兵瑞恩就结束了 - if (u == n * m) return dist[u][st]; + // dis[u][st]:到达了n*m,并且,当前状态是st: 找到大兵瑞恩就结束了,不用管最终的钥匙状态是什么 + // 是什么都是符合拯救大兵的目标的 + if (u == n * m) return dis[u][st]; // 四个方向 for (int i = 0; i < 4; i++) { @@ -144,27 +145,26 @@ int bfs() { int tx = (u - 1) / m + 1 + dx[i]; // 下一个位置 int ty = (u - 1) % m + 1 + dy[i]; - int tz = get(tx, ty); // 要去的坐标位置tz - int ts = st; // 复制出z结点携带过来的钥匙状态 + int T = get(tx, ty); // 要去的坐标位置T /* - g[z][tz] == 0 有墙,不能走 - g[z][tz] > 0 有门,有钥匙能走,无钥匙不能走 - g[z][tz] == -1 随便走 + g[z][T] == 0 有墙,不能走 + g[z][T] > 0 有门,有钥匙能走,无钥匙不能走 + g[z][T] == -1 随便走 */ - // 出界或有墙 - if (tx == 0 || ty == 0 || tx > n || ty > m || g[u][tz] == 0) continue; + // 出界或有墙,没有办法转移 + if (tx == 0 || ty == 0 || tx > n || ty > m || g[u][T] == 0) continue; - // 有门,并且, v这个状态中没有当前类型的钥匙 - if (g[u][tz] > 0 && !(st >> g[u][tz] & 1)) continue; + // 有门,并且, st这个状态中没有带过来当前类型的钥匙 + if (g[u][T] > 0 && !(st >> g[u][T] & 1)) continue; - // 捡起钥匙 - ts |= key[tz]; + // 捡起钥匙不会增加成本,所以,无条件捡起来钥匙 + int ST = st | key[T]; // 如果这个状态没有走过 - if (dist[tz][ts] == INF) { - q.push({tz, ts}); // 入队列 - dist[tz][ts] = dist[u][st] + 1; // 步数加1 + if (dis[T][ST] == INF) { + q.push({T, ST}); // 入队列 + dis[T][ST] = dis[u][st] + 1; // 步数加1 } } } @@ -204,4 +204,5 @@ int main() { printf("%d\n", bfs()); return 0; } + ``` From 4fa408495dfcfdba15b96d23f784478c6e1030f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 2 Jan 2024 08:44:18 +0800 Subject: [PATCH 35/87] 'commit' --- .../AcWing_TiGao/T3/MiniPathExtend/1134.md | 45 +++++++++---------- .../T3/MiniPathExtend/1134_Bfs.cpp | 16 +++---- .../T3/MiniPathExtend/1134_Dijkstra.cpp | 29 ++++++------ 3 files changed, 44 insertions(+), 46 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134.md b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134.md index 289128f..9200b44 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134.md +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134.md @@ -93,26 +93,26 @@ void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; } -int cnt[N]; //从顶点1开始,到其他每个点的最短路有几条 -int dist[N]; //最短距离 +int cnt[N]; // 从顶点1开始,到其他每个点的最短路有几条 +int dis[N]; // 最短距离 int n, m; void bfs() { - memset(dist, 0x3f, sizeof dist); + memset(dis, 0x3f, sizeof dis); queue q; q.push(1); - cnt[1] = 1; //从顶点1开始,到顶点1的最短路有1条 - dist[1] = 0; //距离为0 + cnt[1] = 1; // 从顶点1开始,到顶点1的最短路有1条 + dis[1] = 0; // 距离为0 while (q.size()) { int u = q.front(); q.pop(); for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; - if (dist[j] > dist[u] + 1) { - dist[j] = dist[u] + 1; + if (dis[j] > dis[u] + 1) { + dis[j] = dis[u] + 1; q.push(j); cnt[j] = cnt[u]; - } else if (dist[j] == dist[u] + 1) + } else if (dis[j] == dis[u] + 1) cnt[j] = (cnt[j] + cnt[u]) % MOD; } } @@ -141,7 +141,7 @@ const int MOD = 100003; int n, m; int cnt[N]; -int dist[N]; +int dis[N]; bool st[N]; int h[N], e[M], ne[M], idx; void add(int a, int b) { @@ -149,30 +149,29 @@ void add(int a, int b) { } void dijkstra() { - memset(dist, 0x3f, sizeof dist); - dist[1] = 0; - // 出发点到自己的最短路径只能有1条 - cnt[1] = 1; + memset(dis, 0x3f, sizeof dis); + dis[1] = 0; + cnt[1] = 1; // 出发点到自己的最短路径有1条,长度是0 // 小顶堆q - priority_queue, greater> pq; - pq.push({0, 1}); + priority_queue, greater> q; + q.push({0, 1}); - while (pq.size()) { - auto t = pq.top(); - pq.pop(); - int u = t.second, d = t.first; + while (q.size()) { + auto t = q.top(); + q.pop(); + int u = t.second; if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; - if (dist[j] > d + 1) { - dist[j] = d + 1; + if (dis[j] > dis[u] + 1) { + dis[j] = dis[u] + 1; cnt[j] = cnt[u]; - pq.push({dist[j], j}); - } else if (dist[j] == d + 1) + q.push({dis[j], j}); + } else if (dis[j] == dis[u] + 1) cnt[j] = (cnt[j] + cnt[u]) % MOD; } } diff --git a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134_Bfs.cpp b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134_Bfs.cpp index 240578b..8aeb9d8 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134_Bfs.cpp +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134_Bfs.cpp @@ -7,26 +7,26 @@ void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; } -int cnt[N]; //从顶点1开始,到其他每个点的最短路有几条 -int dist[N]; //最短距离 +int cnt[N]; // 从顶点1开始,到其他每个点的最短路有几条 +int dis[N]; // 最短距离 int n, m; void bfs() { - memset(dist, 0x3f, sizeof dist); + memset(dis, 0x3f, sizeof dis); queue q; q.push(1); - cnt[1] = 1; //从顶点1开始,到顶点1的最短路有1条 - dist[1] = 0; //距离为0 + cnt[1] = 1; // 从顶点1开始,到顶点1的最短路有1条 + dis[1] = 0; // 距离为0 while (q.size()) { int u = q.front(); q.pop(); for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; - if (dist[j] > dist[u] + 1) { - dist[j] = dist[u] + 1; + if (dis[j] > dis[u] + 1) { + dis[j] = dis[u] + 1; q.push(j); cnt[j] = cnt[u]; - } else if (dist[j] == dist[u] + 1) + } else if (dis[j] == dis[u] + 1) cnt[j] = (cnt[j] + cnt[u]) % MOD; } } diff --git a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134_Dijkstra.cpp b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134_Dijkstra.cpp index 8d6e8eb..74d98ab 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134_Dijkstra.cpp +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134_Dijkstra.cpp @@ -6,7 +6,7 @@ const int MOD = 100003; int n, m; int cnt[N]; -int dist[N]; +int dis[N]; bool st[N]; int h[N], e[M], ne[M], idx; void add(int a, int b) { @@ -14,30 +14,29 @@ void add(int a, int b) { } void dijkstra() { - memset(dist, 0x3f, sizeof dist); - dist[1] = 0; - // 出发点到自己的最短路径只能有1条 - cnt[1] = 1; + memset(dis, 0x3f, sizeof dis); + dis[1] = 0; + cnt[1] = 1; // 出发点到自己的最短路径有1条,长度是0 // 小顶堆q - priority_queue, greater> pq; - pq.push({0, 1}); + priority_queue, greater> q; + q.push({0, 1}); - while (pq.size()) { - auto t = pq.top(); - pq.pop(); - int u = t.second, d = t.first; + while (q.size()) { + auto t = q.top(); + q.pop(); + int u = t.second; if (st[u]) continue; st[u] = true; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; - if (dist[j] > d + 1) { - dist[j] = d + 1; + if (dis[j] > dis[u] + 1) { + dis[j] = dis[u] + 1; cnt[j] = cnt[u]; - pq.push({dist[j], j}); - } else if (dist[j] == d + 1) + q.push({dis[j], j}); + } else if (dis[j] == dis[u] + 1) cnt[j] = (cnt[j] + cnt[u]) % MOD; } } From 715e9e6d6e4648a66f71fb89fdc33ad18cebb331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 2 Jan 2024 09:23:41 +0800 Subject: [PATCH 36/87] 'commit' --- TangDou/AcWing/MiniPath/POJ3464.cpp | 102 ----------------- .../AcWing_TiGao/T3/MiniPathExtend/1134.md | 85 +++++++------- .../T3/MiniPathExtend/POJ3463.cpp | 108 ++++++++++++++++++ .../AcWing_TiGao/T3/MiniPathExtend/POJ3463.in | 19 +++ 4 files changed, 173 insertions(+), 141 deletions(-) delete mode 100644 TangDou/AcWing/MiniPath/POJ3464.cpp create mode 100644 TangDou/AcWing_TiGao/T3/MiniPathExtend/POJ3463.cpp create mode 100644 TangDou/AcWing_TiGao/T3/MiniPathExtend/POJ3463.in diff --git a/TangDou/AcWing/MiniPath/POJ3464.cpp b/TangDou/AcWing/MiniPath/POJ3464.cpp deleted file mode 100644 index fb9b641..0000000 --- a/TangDou/AcWing/MiniPath/POJ3464.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include -#include -#include -#include -using namespace std; -#define x first -#define y second - -const int N = 1e3 + 13; -const int M = 1e6 + 10; -int n, m, u, v, s, f; -int dist[N][2], cnt[N][2]; -bool st[N][2]; - -//链式前向星 -int e[M], h[N], idx, w[M], ne[M]; -void add(int a, int b, int c = 0) { - e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; -} - -struct Node { - // u: 节点号 - // d:目前结点v的路径长度 - // k:是最短路0还是次短路1 - int u, d, k; - // POJ中结构体,没有构造函数,直接报编译错误 - Node(int u, int d, int k) { - this->u = u, this->d = d, this->k = k; - } - const bool operator<(Node x) const { - return d > x.d; - } -}; - -void dijkrsta() { - priority_queue q; //通过定义结构体小于号,实现小顶堆 - memset(dist, 0x3f, sizeof(dist)); //清空最小距离与次小距离数组 - memset(cnt, 0, sizeof(cnt)); //清空最小距离路线个数与次小距离路线个数数组 - memset(st, 0, sizeof(st)); //清空是否出队过数组 - - cnt[s][0] = 1; //起点s,0:最短路,1:有一条 - cnt[s][1] = 0; //次短路,路线数为0 - - dist[s][0] = 0; //最短路从s出发到s的距离是0 - dist[s][1] = 0; //次短路从s出发到s的距离是0 - - q.push(Node(s, 0, 0)); //入队列 - - while (q.size()) { - Node x = q.top(); - q.pop(); - - int u = x.u, k = x.k, d = x.d; - - if (st[u][k]) continue; //① - st[u][k] = true; - - for (int i = h[u]; ~i; i = ne[i]) { - int j = e[i]; - int dj = d + w[i]; //原长度+到节点j的边长 - - if (dj == dist[j][0]) //与到j的最短长度相等,则更新路径数量 - cnt[j][0] += cnt[u][k]; - else if (dj < dist[j][0]) { //找到更小的路线,需要更新 - dist[j][1] = dist[j][0]; //次短距离被最短距离覆盖 - cnt[j][1] = cnt[j][0]; //次短个数被最短个数覆盖 - - dist[j][0] = dj; //更新最短距离 - cnt[j][0] = cnt[u][k]; //更新最短个数 - - q.push(Node(j, dist[j][1], 1)); //② - q.push(Node(j, dist[j][0], 0)); - } else if (dj == dist[j][1]) //如果等于次短 - cnt[j][1] += cnt[u][k]; //更新次短的方案数,累加 - else if (dj < dist[j][1]) { //如果大于最短,小于次短,两者中间 - dist[j][1] = dj; //更新次短距离 - cnt[j][1] = cnt[u][k]; //更新次短方案数 - q.push(Node(j, dist[j][1], 1)); //次短入队列 - } - } - } -} -int main() { - int T; - scanf("%d", &T); - while (T--) { - memset(h, -1, sizeof h); - scanf("%d %d", &n, &m); - while (m--) { - int a, b, c; - scanf("%d %d %d", &a, &b, &c); - add(a, b, c); - } - //起点和终点 - scanf("%d %d", &s, &f); - //计算最短路 - dijkrsta(); - //输出 - printf("%d\n", cnt[f][0] + (dist[f][1] == dist[f][0] + 1 ? cnt[f][1] : 0)); - } - return 0; -} diff --git a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134.md b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134.md index 9200b44..dc7ee6a 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134.md +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/1134.md @@ -203,7 +203,7 @@ int main() { **分析** 只需要计算出最短路的条数和距离、次短路的距离和条数,最后判断最短路和次短路的关系即可,在$dijkstra$求最短路的基础上,**加一维** 保存从起点到该点的 **最短路** 和 **次短路**,**同时记录相应的数量** -如果当前点的最短路或次短路更新了,那么这个点可能松弛其他点,加入优先队列;如果等于最短路或次短路,相应的数量就加 +如果当前点的最短路或次短路更新了,那么这个点可能松弛其他点,加入优先队列;如果等于最短路或次短路,相应的数量就加。 关于代码中①②的自我解释: @@ -224,10 +224,12 @@ using namespace std; const int N = 1e3 + 13; const int M = 1e6 + 10; int n, m, u, v, s, f; -int dist[N][2], cnt[N][2]; +// 将最短路扩展为二维,含义:最短路与次短路 +// dis:路径长度,cnt:路线数量,st:是否已经出队列 +int dis[N][2], cnt[N][2]; bool st[N][2]; -//链式前向星 +// 链式前向星 int e[M], h[N], idx, w[M], ne[M]; void add(int a, int b, int c = 0) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; @@ -238,64 +240,68 @@ struct Node { // d:目前结点v的路径长度 // k:是最短路0还是次短路1 int u, d, k; - // POJ中结构体,没有构造函数,直接报编译错误 - Node(int u, int d, int k) { - this->u = u, this->d = d, this->k = k; - } const bool operator<(Node x) const { return d > x.d; } }; void dijkrsta() { - priority_queue q; //通过定义结构体小于号,实现小顶堆 - memset(dist, 0x3f, sizeof(dist)); //清空最小距离与次小距离数组 - memset(cnt, 0, sizeof(cnt)); //清空最小距离路线个数与次小距离路线个数数组 - memset(st, 0, sizeof(st)); //清空是否出队过数组 + priority_queue q; // 默认是大顶堆,通过定义结构体小于号,实现小顶堆。比如:认证的d值更大,谁就更小! + memset(dis, 0x3f, sizeof dis); // 清空最小距离与次小距离数组 + memset(cnt, 0, sizeof cnt); // 清空最小距离路线个数与次小距离路线个数数组 + memset(st, 0, sizeof st); // 清空是否出队过数组 - cnt[s][0] = 1; //起点s,0:最短路,1:有一条 - cnt[s][1] = 0; //次短路,路线数为0 + cnt[s][0] = 1; // 起点s,0:最短路,1:有一条 + cnt[s][1] = 0; // 次短路,路线数为0 - dist[s][0] = 0; //最短路从s出发到s的距离是0 - dist[s][1] = 0; //次短路从s出发到s的距离是0 + dis[s][0] = 0; // 最短路从s出发到s的距离是0 + dis[s][1] = 0; // 次短路从s出发到s的距离是0 - q.push(Node(s, 0, 0)); //入队列 + q.push({s, 0, 0}); // 入队列 while (q.size()) { Node x = q.top(); q.pop(); - int u = x.u, k = x.k, d = x.d; + int u = x.u, k = x.k; // u:节点号,k:是最短路还是次短路,d:路径长度(这个主要用于堆中排序,不用于实战,实战中可以使用dis[u][k]) - if (st[u][k]) continue; //① + if (st[u][k]) continue; // ① 和dijkstra标准版本一样的,只不过多了一个维度 st[u][k] = true; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; - int dj = d + w[i]; //原长度+到节点j的边长 + int dj = dis[u][k] + w[i]; // 原长度+到节点j的边长 - if (dj == dist[j][0]) //与到j的最短长度相等,则更新路径数量 + if (dj == dis[j][0]) // 与到j的最短长度相等,则更新路径数量 cnt[j][0] += cnt[u][k]; - else if (dj < dist[j][0]) { //找到更小的路线,需要更新 - dist[j][1] = dist[j][0]; //次短距离被最短距离覆盖 - cnt[j][1] = cnt[j][0]; //次短个数被最短个数覆盖 - - dist[j][0] = dj; //更新最短距离 - cnt[j][0] = cnt[u][k]; //更新最短个数 - - q.push(Node(j, dist[j][1], 1)); //② - q.push(Node(j, dist[j][0], 0)); - } else if (dj == dist[j][1]) //如果等于次短 - cnt[j][1] += cnt[u][k]; //更新次短的方案数,累加 - else if (dj < dist[j][1]) { //如果大于最短,小于次短,两者中间 - dist[j][1] = dj; //更新次短距离 - cnt[j][1] = cnt[u][k]; //更新次短方案数 - q.push(Node(j, dist[j][1], 1)); //次短入队列 + else if (dj < dis[j][0]) { // 找到更小的路线,需要更新 + dis[j][1] = dis[j][0]; // 次短距离被最短距离覆盖 + cnt[j][1] = cnt[j][0]; // 次短个数被最短个数覆盖 + + dis[j][0] = dj; // 更新最短距离 + cnt[j][0] = cnt[u][k]; // 更新最短个数 + + q.push({j, dis[j][1], 1}); // ② + q.push({j, dis[j][0], 0}); + } else if (dj == dis[j][1]) // 如果等于次短 + cnt[j][1] += cnt[u][k]; // 更新次短的方案数,累加 + else if (dj < dis[j][1]) { // 如果大于最短,小于次短,两者中间 + dis[j][1] = dj; // 更新次短距离 + cnt[j][1] = cnt[u][k]; // 更新次短方案数 + q.push({j, dis[j][1], 1}); // 次短入队列 } } } } int main() { +#ifndef ONLINE_JUDGE + freopen("POJ3463.in", "r", stdin); + /* + 答案: + 3 + 2 + */ +#endif int T; scanf("%d", &T); while (T--) { @@ -306,13 +312,14 @@ int main() { scanf("%d %d %d", &a, &b, &c); add(a, b, c); } - //起点和终点 + // 起点和终点 scanf("%d %d", &s, &f); - //计算最短路 + // 计算最短路 dijkrsta(); - //输出 - printf("%d\n", cnt[f][0] + (dist[f][1] == dist[f][0] + 1 ? cnt[f][1] : 0)); + // 输出 + printf("%d\n", cnt[f][0] + (dis[f][1] == dis[f][0] + 1 ? cnt[f][1] : 0)); } return 0; } + ``` diff --git a/TangDou/AcWing_TiGao/T3/MiniPathExtend/POJ3463.cpp b/TangDou/AcWing_TiGao/T3/MiniPathExtend/POJ3463.cpp new file mode 100644 index 0000000..172785f --- /dev/null +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/POJ3463.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include +using namespace std; +#define x first +#define y second + +const int N = 1e3 + 13; +const int M = 1e6 + 10; +int n, m, u, v, s, f; +// 将最短路扩展为二维,含义:最短路与次短路 +// dis:路径长度,cnt:路线数量,st:是否已经出队列 +int dis[N][2], cnt[N][2]; +bool st[N][2]; + +// 链式前向星 +int e[M], h[N], idx, w[M], ne[M]; +void add(int a, int b, int c = 0) { + e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; +} + +struct Node { + // u: 节点号 + // d:目前结点v的路径长度 + // k:是最短路0还是次短路1 + int u, d, k; + const bool operator<(Node x) const { + return d > x.d; + } +}; + +void dijkrsta() { + priority_queue q; // 默认是大顶堆,通过定义结构体小于号,实现小顶堆。比如:认证的d值更大,谁就更小! + memset(dis, 0x3f, sizeof dis); // 清空最小距离与次小距离数组 + memset(cnt, 0, sizeof cnt); // 清空最小距离路线个数与次小距离路线个数数组 + memset(st, 0, sizeof st); // 清空是否出队过数组 + + cnt[s][0] = 1; // 起点s,0:最短路,1:有一条 + cnt[s][1] = 0; // 次短路,路线数为0 + + dis[s][0] = 0; // 最短路从s出发到s的距离是0 + dis[s][1] = 0; // 次短路从s出发到s的距离是0 + + q.push({s, 0, 0}); // 入队列 + + while (q.size()) { + Node x = q.top(); + q.pop(); + + int u = x.u, k = x.k; // u:节点号,k:是最短路还是次短路,d:路径长度(这个主要用于堆中排序,不用于实战,实战中可以使用dis[u][k]) + + if (st[u][k]) continue; // ① 和dijkstra标准版本一样的,只不过多了一个维度 + st[u][k] = true; + + for (int i = h[u]; ~i; i = ne[i]) { + int j = e[i]; + int dj = dis[u][k] + w[i]; // 原长度+到节点j的边长 + + if (dj == dis[j][0]) // 与到j的最短长度相等,则更新路径数量 + cnt[j][0] += cnt[u][k]; + else if (dj < dis[j][0]) { // 找到更小的路线,需要更新 + dis[j][1] = dis[j][0]; // 次短距离被最短距离覆盖 + cnt[j][1] = cnt[j][0]; // 次短个数被最短个数覆盖 + + dis[j][0] = dj; // 更新最短距离 + cnt[j][0] = cnt[u][k]; // 更新最短个数 + + q.push({j, dis[j][1], 1}); // ② + q.push({j, dis[j][0], 0}); + } else if (dj == dis[j][1]) // 如果等于次短 + cnt[j][1] += cnt[u][k]; // 更新次短的方案数,累加 + else if (dj < dis[j][1]) { // 如果大于最短,小于次短,两者中间 + dis[j][1] = dj; // 更新次短距离 + cnt[j][1] = cnt[u][k]; // 更新次短方案数 + q.push({j, dis[j][1], 1}); // 次短入队列 + } + } + } +} +int main() { +#ifndef ONLINE_JUDGE + freopen("POJ3463.in", "r", stdin); + /* + 答案: + 3 + 2 + */ +#endif + int T; + scanf("%d", &T); + while (T--) { + memset(h, -1, sizeof h); + scanf("%d %d", &n, &m); + while (m--) { + int a, b, c; + scanf("%d %d %d", &a, &b, &c); + add(a, b, c); + } + // 起点和终点 + scanf("%d %d", &s, &f); + // 计算最短路 + dijkrsta(); + // 输出 + printf("%d\n", cnt[f][0] + (dis[f][1] == dis[f][0] + 1 ? cnt[f][1] : 0)); + } + return 0; +} diff --git a/TangDou/AcWing_TiGao/T3/MiniPathExtend/POJ3463.in b/TangDou/AcWing_TiGao/T3/MiniPathExtend/POJ3463.in new file mode 100644 index 0000000..e0fe637 --- /dev/null +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/POJ3463.in @@ -0,0 +1,19 @@ +2 +5 8 +1 2 3 +1 3 2 +1 4 5 +2 3 1 +2 5 3 +3 4 2 +3 5 4 +4 5 3 +1 5 +5 6 +2 3 1 +3 2 1 +3 1 10 +4 5 2 +5 2 7 +5 2 7 +4 1 \ No newline at end of file From dec87d32b541f441e1f12d0d554deb7b273325c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 2 Jan 2024 09:33:44 +0800 Subject: [PATCH 37/87] 'commit' --- .../AcWing_TiGao/T3/MiniPathExtend/383.cpp | 112 ++++++++++-------- 1 file changed, 60 insertions(+), 52 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/MiniPathExtend/383.cpp b/TangDou/AcWing_TiGao/T3/MiniPathExtend/383.cpp index c182de6..d854526 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathExtend/383.cpp +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/383.cpp @@ -1,12 +1,14 @@ #include using namespace std; +#define x first +#define y second -const int N = 1010; -const int M = 10010; -int n, m; - -int dist[N][2]; -int cnt[N][2]; +const int N = 1e3 + 13; +const int M = 1e6 + 10; +int n, m, u, v, s, f; +// 将最短路扩展为二维,含义:最短路与次短路 +// dis:路径长度,cnt:路线数量,st:是否已经出队列 +int dis[N][2], cnt[N][2]; bool st[N][2]; // 链式前向星 @@ -15,53 +17,61 @@ void add(int a, int b, int c = 0) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } -// 本题需要一个三个属性的对象:最短距离d,最短、次短k,id:节点号 struct Node { - int d, k, id; - // 小顶堆需要重载大于号,大顶堆需要重载小于号 - bool const operator>(Node b) const { - return d > b.d; + // u: 节点号 + // d:目前结点v的路径长度 + // k:是最短路0还是次短路1 + int u, d, k; + const bool operator<(Node x) const { + return d > x.d; } }; -void dijkstra(int S) { - memset(dist, 0x3f, sizeof dist); - memset(st, false, sizeof st); - memset(cnt, 0, sizeof cnt); - priority_queue, greater<>> pq; // 小顶堆 - dist[S][0] = 0; - cnt[S][0] = 1; - pq.push({0, 0, S}); +void dijkrsta() { + priority_queue q; // 默认是大顶堆,通过定义结构体小于号,实现小顶堆。比如:认证的d值更大,谁就更小! + memset(dis, 0x3f, sizeof dis); // 清空最小距离与次小距离数组 + memset(cnt, 0, sizeof cnt); // 清空最小距离路线个数与次小距离路线个数数组 + memset(st, 0, sizeof st); // 清空是否出队过数组 + + cnt[s][0] = 1; // 起点s,0:最短路,1:有一条 + cnt[s][1] = 0; // 次短路,路线数为0 + + dis[s][0] = 0; // 最短路从s出发到s的距离是0 + dis[s][1] = 0; // 次短路从s出发到s的距离是0 + + q.push({s, 0, 0}); // 入队列 + + while (q.size()) { + Node x = q.top(); + q.pop(); - while (pq.size()) { - auto t = pq.top(); - pq.pop(); - int u = t.id; - int k = t.k; + int u = x.u, k = x.k; // u:节点号,k:是最短路还是次短路,d:路径长度(这个主要用于堆中排序,不用于实战,实战中可以使用dis[u][k]) - if (st[u][k]) continue; + if (st[u][k]) continue; // ① 和dijkstra标准版本一样的,只不过多了一个维度 st[u][k] = true; for (int i = h[u]; ~i; i = ne[i]) { - int v = e[i]; - int d = dist[u][k] + w[i]; + int j = e[i]; + int dj = dis[u][k] + w[i]; // 原长度+到节点j的边长 - if (dist[v][0] > d) { // 比最短路还要短 - dist[v][1] = dist[v][0]; // 最短降为次短 - cnt[v][1] = cnt[v][0]; // 次短路数量被更新 - pq.push({dist[v][1], 1, v}); // 次短被更新,次短入队列 + if (dj == dis[j][0]) // 与到j的最短长度相等,则更新路径数量 + cnt[j][0] += cnt[u][k]; + else if (dj < dis[j][0]) { // 找到更小的路线,需要更新 + dis[j][1] = dis[j][0]; // 次短距离被最短距离覆盖 + cnt[j][1] = cnt[j][0]; // 次短个数被最短个数覆盖 - dist[v][0] = d; // 替换最短路 - cnt[v][0] = cnt[u][k]; // 替换最短路数量 - pq.push({dist[v][0], 0, v}); // 最短路入队列 - } else if (dist[v][0] == d) // 增加最短路的数量 - cnt[v][0] += cnt[u][k]; - else if (dist[v][1] > d) { // 替换次短路 - dist[v][1] = d; - cnt[v][1] = cnt[u][k]; - pq.push({dist[v][1], 1, v}); // 次短路入队列 - } else if (dist[v][1] == d) - cnt[v][1] += cnt[u][k]; + dis[j][0] = dj; // 更新最短距离 + cnt[j][0] = cnt[u][k]; // 更新最短个数 + + q.push({j, dis[j][1], 1}); // ② + q.push({j, dis[j][0], 0}); + } else if (dj == dis[j][1]) // 如果等于次短 + cnt[j][1] += cnt[u][k]; // 更新次短的方案数,累加 + else if (dj < dis[j][1]) { // 如果大于最短,小于次短,两者中间 + dis[j][1] = dj; // 更新次短距离 + cnt[j][1] = cnt[u][k]; // 更新次短方案数 + q.push({j, dis[j][1], 1}); // 次短入队列 + } } } } @@ -69,21 +79,19 @@ int main() { int T; scanf("%d", &T); while (T--) { - scanf("%d %d", &n, &m); memset(h, -1, sizeof h); - idx = 0; + scanf("%d %d", &n, &m); while (m--) { int a, b, c; scanf("%d %d %d", &a, &b, &c); add(a, b, c); } - int S, F; - scanf("%d %d", &S, &F); - dijkstra(S); - int ans = cnt[F][0]; // 最短路 - // 在正常处理完最短路和次短路后,在最后的逻辑中,增加本题的中特殊要求部分 - if (dist[F][0] == dist[F][1] - 1) ans += cnt[F][1]; - printf("%d\n", ans); + // 起点和终点 + scanf("%d %d", &s, &f); + // 计算最短路 + dijkrsta(); + // 输出 + printf("%d\n", cnt[f][0] + (dis[f][1] == dis[f][0] + 1 ? cnt[f][1] : 0)); } return 0; -} \ No newline at end of file +} From 41a717db9f071adf55600c34af480d8cc5487fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 2 Jan 2024 09:33:54 +0800 Subject: [PATCH 38/87] 'commit' --- TangDou/AcWing_TiGao/T3/MiniPathExtend/383.md | 121 ++++++++++-------- 1 file changed, 65 insertions(+), 56 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/MiniPathExtend/383.md b/TangDou/AcWing_TiGao/T3/MiniPathExtend/383.md index 9ae09bd..d07724a 100644 --- a/TangDou/AcWing_TiGao/T3/MiniPathExtend/383.md +++ b/TangDou/AcWing_TiGao/T3/MiniPathExtend/383.md @@ -119,13 +119,15 @@ $dist[S][0]$=$0$,$cnt[S][0]$=$1$ ```cpp {.line-numbers} #include using namespace std; - -const int N = 1010; -const int M = 10010; -int n, m; - -int dist[N][2]; -int cnt[N][2]; +#define x first +#define y second + +const int N = 1e3 + 13; +const int M = 1e6 + 10; +int n, m, u, v, s, f; +// 将最短路扩展为二维,含义:最短路与次短路 +// dis:路径长度,cnt:路线数量,st:是否已经出队列 +int dis[N][2], cnt[N][2]; bool st[N][2]; // 链式前向星 @@ -134,53 +136,61 @@ void add(int a, int b, int c = 0) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } -// 本题需要一个三个属性的对象:最短距离d,最短、次短k,id:节点号 struct Node { - int d, k, id; - // 小顶堆需要重载大于号,大顶堆需要重载小于号 - bool const operator>(Node b) const { - return d > b.d; + // u: 节点号 + // d:目前结点v的路径长度 + // k:是最短路0还是次短路1 + int u, d, k; + const bool operator<(Node x) const { + return d > x.d; } }; -void dijkstra(int S) { - memset(dist, 0x3f, sizeof dist); - memset(st, false, sizeof st); - memset(cnt, 0, sizeof cnt); - priority_queue, greater<>> pq; // 小顶堆 - dist[S][0] = 0; - cnt[S][0] = 1; - pq.push({0, 0, S}); - - while (pq.size()) { - auto t = pq.top(); - pq.pop(); - int u = t.id; - int k = t.k; - - if (st[u][k]) continue; +void dijkrsta() { + priority_queue q; // 默认是大顶堆,通过定义结构体小于号,实现小顶堆。比如:认证的d值更大,谁就更小! + memset(dis, 0x3f, sizeof dis); // 清空最小距离与次小距离数组 + memset(cnt, 0, sizeof cnt); // 清空最小距离路线个数与次小距离路线个数数组 + memset(st, 0, sizeof st); // 清空是否出队过数组 + + cnt[s][0] = 1; // 起点s,0:最短路,1:有一条 + cnt[s][1] = 0; // 次短路,路线数为0 + + dis[s][0] = 0; // 最短路从s出发到s的距离是0 + dis[s][1] = 0; // 次短路从s出发到s的距离是0 + + q.push({s, 0, 0}); // 入队列 + + while (q.size()) { + Node x = q.top(); + q.pop(); + + int u = x.u, k = x.k; // u:节点号,k:是最短路还是次短路,d:路径长度(这个主要用于堆中排序,不用于实战,实战中可以使用dis[u][k]) + + if (st[u][k]) continue; // ① 和dijkstra标准版本一样的,只不过多了一个维度 st[u][k] = true; for (int i = h[u]; ~i; i = ne[i]) { - int v = e[i]; - int d = dist[u][k] + w[i]; - - if (dist[v][0] > d) { // 比最短路还要短 - dist[v][1] = dist[v][0]; // 最短降为次短 - cnt[v][1] = cnt[v][0]; // 次短路数量被更新 - pq.push({dist[v][1], 1, v}); // 次短被更新,次短入队列 - - dist[v][0] = d; // 替换最短路 - cnt[v][0] = cnt[u][k]; // 替换最短路数量 - pq.push({dist[v][0], 0, v}); // 最短路入队列 - } else if (dist[v][0] == d) // 增加最短路的数量 - cnt[v][0] += cnt[u][k]; - else if (dist[v][1] > d) { // 替换次短路 - dist[v][1] = d; - cnt[v][1] = cnt[u][k]; - pq.push({dist[v][1], 1, v}); // 次短路入队列 - } else if (dist[v][1] == d) - cnt[v][1] += cnt[u][k]; + int j = e[i]; + int dj = dis[u][k] + w[i]; // 原长度+到节点j的边长 + + if (dj == dis[j][0]) // 与到j的最短长度相等,则更新路径数量 + cnt[j][0] += cnt[u][k]; + else if (dj < dis[j][0]) { // 找到更小的路线,需要更新 + dis[j][1] = dis[j][0]; // 次短距离被最短距离覆盖 + cnt[j][1] = cnt[j][0]; // 次短个数被最短个数覆盖 + + dis[j][0] = dj; // 更新最短距离 + cnt[j][0] = cnt[u][k]; // 更新最短个数 + + q.push({j, dis[j][1], 1}); // ② + q.push({j, dis[j][0], 0}); + } else if (dj == dis[j][1]) // 如果等于次短 + cnt[j][1] += cnt[u][k]; // 更新次短的方案数,累加 + else if (dj < dis[j][1]) { // 如果大于最短,小于次短,两者中间 + dis[j][1] = dj; // 更新次短距离 + cnt[j][1] = cnt[u][k]; // 更新次短方案数 + q.push({j, dis[j][1], 1}); // 次短入队列 + } } } } @@ -188,22 +198,21 @@ int main() { int T; scanf("%d", &T); while (T--) { - scanf("%d %d", &n, &m); memset(h, -1, sizeof h); - idx = 0; + scanf("%d %d", &n, &m); while (m--) { int a, b, c; scanf("%d %d %d", &a, &b, &c); add(a, b, c); } - int S, F; - scanf("%d %d", &S, &F); - dijkstra(S); - int ans = cnt[F][0]; // 最短路 - // 在正常处理完最短路和次短路后,在最后的逻辑中,增加本题的中特殊要求部分 - if (dist[F][0] == dist[F][1] - 1) ans += cnt[F][1]; - printf("%d\n", ans); + // 起点和终点 + scanf("%d %d", &s, &f); + // 计算最短路 + dijkrsta(); + // 输出 + printf("%d\n", cnt[f][0] + (dis[f][1] == dis[f][0] + 1 ? cnt[f][1] : 0)); } return 0; } + ``` \ No newline at end of file From 2af705fa0dd501c50cee75329286b7fa2bfd0218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 2 Jan 2024 13:06:31 +0800 Subject: [PATCH 39/87] 'commit' --- TangDou/AcWing/DisjointSet/1250.cpp | 50 ------------------- TangDou/AcWing_TiGao/T4/1250.cpp | 50 +++++++++++++++++++ .../DisjointSet => AcWing_TiGao/T4}/1250.md | 31 ++++++------ .../DisjointSet => AcWing_TiGao/T4}/1252.cpp | 0 .../DisjointSet => AcWing_TiGao/T4}/1252.md | 0 .../T4}/1252_ErWei.cpp | 0 .../DisjointSet => AcWing_TiGao/T4}/237.cpp | 0 .../DisjointSet => AcWing_TiGao/T4}/237.md | 0 .../DisjointSet => AcWing_TiGao/T4}/238.cpp | 0 .../DisjointSet => AcWing_TiGao/T4}/238.md | 0 .../DisjointSet => AcWing_TiGao/T4}/239.md | 0 .../T4}/239_DaiQuan_STL.cpp | 0 .../T4}/239_DaiQuan_Static_ErFen.cpp | 0 .../T4}/239_ExtendDomain_STL.cpp | 0 .../T4}/239_ExtendDomain_Static_ErFen.cpp | 0 15 files changed, 65 insertions(+), 66 deletions(-) delete mode 100644 TangDou/AcWing/DisjointSet/1250.cpp create mode 100644 TangDou/AcWing_TiGao/T4/1250.cpp rename TangDou/{AcWing/DisjointSet => AcWing_TiGao/T4}/1250.md (68%) rename TangDou/{AcWing/DisjointSet => AcWing_TiGao/T4}/1252.cpp (100%) rename TangDou/{AcWing/DisjointSet => AcWing_TiGao/T4}/1252.md (100%) rename TangDou/{AcWing/DisjointSet => AcWing_TiGao/T4}/1252_ErWei.cpp (100%) rename TangDou/{AcWing/DisjointSet => AcWing_TiGao/T4}/237.cpp (100%) rename TangDou/{AcWing/DisjointSet => AcWing_TiGao/T4}/237.md (100%) rename TangDou/{AcWing/DisjointSet => AcWing_TiGao/T4}/238.cpp (100%) rename TangDou/{AcWing/DisjointSet => AcWing_TiGao/T4}/238.md (100%) rename TangDou/{AcWing/DisjointSet => AcWing_TiGao/T4}/239.md (100%) rename TangDou/{AcWing/DisjointSet => AcWing_TiGao/T4}/239_DaiQuan_STL.cpp (100%) rename TangDou/{AcWing/DisjointSet => AcWing_TiGao/T4}/239_DaiQuan_Static_ErFen.cpp (100%) rename TangDou/{AcWing/DisjointSet => AcWing_TiGao/T4}/239_ExtendDomain_STL.cpp (100%) rename TangDou/{AcWing/DisjointSet => AcWing_TiGao/T4}/239_ExtendDomain_Static_ErFen.cpp (100%) diff --git a/TangDou/AcWing/DisjointSet/1250.cpp b/TangDou/AcWing/DisjointSet/1250.cpp deleted file mode 100644 index 3f0bb7f..0000000 --- a/TangDou/AcWing/DisjointSet/1250.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include -using namespace std; - -const int N = 200 * 200 + 10; - -int n, m; -int p[N]; - -//二维转一维的办法,坐标从(1,1)开始 -inline int get(int x, int y) { - return (x - 1) * n + y; -} - -//最简并查集 -int find(int x) { - if (p[x] != x) p[x] = find(p[x]); //路径压缩 - return p[x]; -} -int main() { - cin >> n >> m; - for (int i = 1; i <= n * n; i++) p[i] = i; - - int res = 0; - for (int i = 1; i <= m; i++) { - int x, y; - char d; - cin >> x >> y >> d; - int a = get(x, y); //计算a点点号 - int b; - if (d == 'D') //向下走 - b = get(x + 1, y); - else //向右走 - b = get(x, y + 1); - - // a,b需要两次相遇,才是出现了环~ - int pa = find(a), pb = find(b); - if (pa == pb) { - res = i; //记录操作步数 - break; - } - //合并并查集 - p[pa] = pb; - } - - if (!res) //没有修改过这个值 - puts("draw"); //平局 - else //输出操作步数 - printf("%d\n", res); - return 0; -} \ No newline at end of file diff --git a/TangDou/AcWing_TiGao/T4/1250.cpp b/TangDou/AcWing_TiGao/T4/1250.cpp new file mode 100644 index 0000000..191abe0 --- /dev/null +++ b/TangDou/AcWing_TiGao/T4/1250.cpp @@ -0,0 +1,50 @@ +#include +using namespace std; + +const int N = 200 * 200 + 10; + +int n, m; +int p[N]; + +// 二维转一维的办法,坐标从(1,1)开始 +int get(int x, int y) { + return (x - 1) * n + y; +} + +// 最简并查集 +int find(int x) { + if (p[x] != x) p[x] = find(p[x]); // 路径压缩 + return p[x]; +} +int main() { + scanf("%d %d", &n, &m); + for (int i = 1; i <= n * n; i++) p[i] = i; // 并查集初始化 + + int res = 0; + for (int i = 1; i <= m; i++) { + int x, y; + char d; + cin >> x >> y >> d; + int a = get(x, y); // 计算a点点号 + int b; + if (d == 'D') // 向下走 + b = get(x + 1, y); + else // 向右走 + b = get(x, y + 1); + + // a,b需要两次相遇,才是出现了环~ + int pa = find(a), pb = find(b); + if (pa == pb) { + res = i; // 记录操作步数 + break; + } + // 合并并查集 + p[pa] = pb; + } + + if (!res) // 没有修改过这个值 + puts("draw"); // 平局 + else // 输出操作步数 + printf("%d\n", res); + return 0; +} \ No newline at end of file diff --git a/TangDou/AcWing/DisjointSet/1250.md b/TangDou/AcWing_TiGao/T4/1250.md similarity index 68% rename from TangDou/AcWing/DisjointSet/1250.md rename to TangDou/AcWing_TiGao/T4/1250.md index 5841525..d5eae4d 100644 --- a/TangDou/AcWing/DisjointSet/1250.md +++ b/TangDou/AcWing_TiGao/T4/1250.md @@ -45,9 +45,8 @@ $1≤n≤200, ``` ### 二、解题思路 -要想形成环,必须保证正在连的这条边,将原来已经半封闭的一个“半环”连通,即原来这个半环是一集合,$a,b$再次相边,那么之前$a,b$在同一集合中。 -**总结**: **判断是否成环就直接判断他们没连接前他们的祖宗结点是否一致,如果一致连接起来就必然成为环。** +**判断是否成环,可以判断他们没连接前,他们的祖宗结点是否一致,如果一致,连接起来就必然成环。** ### 三、实现代码 ```cpp {.line-numbers} @@ -59,45 +58,45 @@ const int N = 200 * 200 + 10; int n, m; int p[N]; -//二维转一维的办法,坐标从(1,1)开始 -inline int get(int x, int y) { +// 二维转一维的办法,坐标从(1,1)开始 +int get(int x, int y) { return (x - 1) * n + y; } -//最简并查集 +// 最简并查集 int find(int x) { - if (p[x] != x) p[x] = find(p[x]); //路径压缩 + if (p[x] != x) p[x] = find(p[x]); // 路径压缩 return p[x]; } int main() { - cin >> n >> m; - for (int i = 1; i <= n * n; i++) p[i] = i; + scanf("%d %d", &n, &m); + for (int i = 1; i <= n * n; i++) p[i] = i; // 并查集初始化 int res = 0; for (int i = 1; i <= m; i++) { int x, y; char d; cin >> x >> y >> d; - int a = get(x, y); //计算a点点号 + int a = get(x, y); // 计算a点点号 int b; - if (d == 'D') //向下走 + if (d == 'D') // 向下走 b = get(x + 1, y); - else //向右走 + else // 向右走 b = get(x, y + 1); // a,b需要两次相遇,才是出现了环~ int pa = find(a), pb = find(b); if (pa == pb) { - res = i; //记录操作步数 + res = i; // 记录操作步数 break; } - //合并并查集 + // 合并并查集 p[pa] = pb; } - if (!res) //没有修改过这个值 - puts("draw"); //平局 - else //输出操作步数 + if (!res) // 没有修改过这个值 + puts("draw"); // 平局 + else // 输出操作步数 printf("%d\n", res); return 0; } diff --git a/TangDou/AcWing/DisjointSet/1252.cpp b/TangDou/AcWing_TiGao/T4/1252.cpp similarity index 100% rename from TangDou/AcWing/DisjointSet/1252.cpp rename to TangDou/AcWing_TiGao/T4/1252.cpp diff --git a/TangDou/AcWing/DisjointSet/1252.md b/TangDou/AcWing_TiGao/T4/1252.md similarity index 100% rename from TangDou/AcWing/DisjointSet/1252.md rename to TangDou/AcWing_TiGao/T4/1252.md diff --git a/TangDou/AcWing/DisjointSet/1252_ErWei.cpp b/TangDou/AcWing_TiGao/T4/1252_ErWei.cpp similarity index 100% rename from TangDou/AcWing/DisjointSet/1252_ErWei.cpp rename to TangDou/AcWing_TiGao/T4/1252_ErWei.cpp diff --git a/TangDou/AcWing/DisjointSet/237.cpp b/TangDou/AcWing_TiGao/T4/237.cpp similarity index 100% rename from TangDou/AcWing/DisjointSet/237.cpp rename to TangDou/AcWing_TiGao/T4/237.cpp diff --git a/TangDou/AcWing/DisjointSet/237.md b/TangDou/AcWing_TiGao/T4/237.md similarity index 100% rename from TangDou/AcWing/DisjointSet/237.md rename to TangDou/AcWing_TiGao/T4/237.md diff --git a/TangDou/AcWing/DisjointSet/238.cpp b/TangDou/AcWing_TiGao/T4/238.cpp similarity index 100% rename from TangDou/AcWing/DisjointSet/238.cpp rename to TangDou/AcWing_TiGao/T4/238.cpp diff --git a/TangDou/AcWing/DisjointSet/238.md b/TangDou/AcWing_TiGao/T4/238.md similarity index 100% rename from TangDou/AcWing/DisjointSet/238.md rename to TangDou/AcWing_TiGao/T4/238.md diff --git a/TangDou/AcWing/DisjointSet/239.md b/TangDou/AcWing_TiGao/T4/239.md similarity index 100% rename from TangDou/AcWing/DisjointSet/239.md rename to TangDou/AcWing_TiGao/T4/239.md diff --git a/TangDou/AcWing/DisjointSet/239_DaiQuan_STL.cpp b/TangDou/AcWing_TiGao/T4/239_DaiQuan_STL.cpp similarity index 100% rename from TangDou/AcWing/DisjointSet/239_DaiQuan_STL.cpp rename to TangDou/AcWing_TiGao/T4/239_DaiQuan_STL.cpp diff --git a/TangDou/AcWing/DisjointSet/239_DaiQuan_Static_ErFen.cpp b/TangDou/AcWing_TiGao/T4/239_DaiQuan_Static_ErFen.cpp similarity index 100% rename from TangDou/AcWing/DisjointSet/239_DaiQuan_Static_ErFen.cpp rename to TangDou/AcWing_TiGao/T4/239_DaiQuan_Static_ErFen.cpp diff --git a/TangDou/AcWing/DisjointSet/239_ExtendDomain_STL.cpp b/TangDou/AcWing_TiGao/T4/239_ExtendDomain_STL.cpp similarity index 100% rename from TangDou/AcWing/DisjointSet/239_ExtendDomain_STL.cpp rename to TangDou/AcWing_TiGao/T4/239_ExtendDomain_STL.cpp diff --git a/TangDou/AcWing/DisjointSet/239_ExtendDomain_Static_ErFen.cpp b/TangDou/AcWing_TiGao/T4/239_ExtendDomain_Static_ErFen.cpp similarity index 100% rename from TangDou/AcWing/DisjointSet/239_ExtendDomain_Static_ErFen.cpp rename to TangDou/AcWing_TiGao/T4/239_ExtendDomain_Static_ErFen.cpp From 5ff0ff7979dbd9f3c84b76e10f6e06d6a5a1f43e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 2 Jan 2024 14:23:37 +0800 Subject: [PATCH 40/87] 'commit' --- TangDou/AcWing_TiGao/T4/1252.cpp | 6 +++--- TangDou/AcWing_TiGao/T4/1252.md | 32 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/TangDou/AcWing_TiGao/T4/1252.cpp b/TangDou/AcWing_TiGao/T4/1252.cpp index 7583878..a01d631 100644 --- a/TangDou/AcWing_TiGao/T4/1252.cpp +++ b/TangDou/AcWing_TiGao/T4/1252.cpp @@ -14,13 +14,13 @@ int find(int x) { } int main() { - cin >> n >> m >> sum; + scanf("%d %d %d", &n, &m, &sum); // 初始化并查集 for (int i = 1; i <= n; i++) p[i] = i; // 读入每个云朵的价钱(体积)和价值 for (int i = 1; i <= n; i++) - cin >> v[i] >> w[i]; + scanf("%d %d", &v[i], &w[i]); while (m--) { int a, b; @@ -35,7 +35,7 @@ int main() { } // 01背包 // 注意:这里不能认为一维的能AC,二维的替代写法就一定能AC - // 这是因为这里的判断p[i]==i,导致i不一定是连通的, + // 这是因为这里的判断p[i]==i,导致i不一定是连续的, // 所以f[i][j]=f[i-1][j]这句话就不一定对 // 所以,看来终极版本的01背包一维解法还是有一定价值的。 for (int i = 1; i <= n; i++) diff --git a/TangDou/AcWing_TiGao/T4/1252.md b/TangDou/AcWing_TiGao/T4/1252.md index 1a196f2..f745490 100644 --- a/TangDou/AcWing_TiGao/T4/1252.md +++ b/TangDou/AcWing_TiGao/T4/1252.md @@ -49,32 +49,32 @@ $1≤n≤10000,0≤m≤5000,1≤w≤10000,1≤ci≤5000,1≤di≤100,1≤u_i,v_i using namespace std; const int N = 10010; -int n, m, sum; //有 n 朵云,m 个搭配,Joe有 sum 的钱。 -int v[N], w[N]; //表示 i 朵云的价钱和价值 -int p[N]; -int f[N]; +int n, m, sum; // 有 n 朵云,m 个搭配,Joe有 sum 的钱。 +int v[N], w[N]; // 表示 i 朵云的价钱和价值 +int p[N]; // 并查集数组 +int f[N]; // 01背包数组 -//最简并查集 +// 最简并查集 int find(int x) { - if (p[x] != x) p[x] = find(p[x]); //路径压缩 + if (p[x] != x) p[x] = find(p[x]); // 路径压缩 return p[x]; } int main() { - cin >> n >> m >> sum; - //初始化并查集 + scanf("%d %d %d", &n, &m, &sum); + // 初始化并查集 for (int i = 1; i <= n; i++) p[i] = i; - //读入每个云朵的价钱(体积)和价值 + // 读入每个云朵的价钱(体积)和价值 for (int i = 1; i <= n; i++) - cin >> v[i] >> w[i]; + scanf("%d %d", &v[i], &w[i]); while (m--) { int a, b; - cin >> a >> b; //两种云朵需要一起买 + cin >> a >> b; // 两种云朵需要一起买 int pa = find(a), pb = find(b); if (pa != pb) { - //集合有两个属性:总价钱、总价值,都记录到root节点上 + // 集合有两个属性:总价钱、总价值,都记录到root节点上 v[pb] += v[pa]; w[pb] += w[pa]; p[pa] = pb; @@ -82,14 +82,14 @@ int main() { } // 01背包 // 注意:这里不能认为一维的能AC,二维的替代写法就一定能AC - // 这是因为这里的判断p[i]==i,导致i不一定是连通的, + // 这是因为这里的判断p[i]==i,导致i不一定是连续的, // 所以f[i][j]=f[i-1][j]这句话就不一定对 // 所以,看来终极版本的01背包一维解法还是有一定价值的。 for (int i = 1; i <= n; i++) - if (p[i] == i) //只关心集合代表元素,选择一组 - for (int j = sum; j >= v[i]; j--) //体积由大到小,倒序,01背包 + if (p[i] == i) // 只关心集合代表元素,选择一组 + for (int j = sum; j >= v[i]; j--) // 体积由大到小,倒序,01背包 f[j] = max(f[j], f[j - v[i]] + w[i]); - //输出最大容量下获取到的价值 + // 输出最大容量下获取到的价值 printf("%d\n", f[sum]); return 0; } From e74778b9dd3c704d1f271786fa0f28455a648254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 3 Jan 2024 08:09:25 +0800 Subject: [PATCH 41/87] 'commit' --- .../T3}/Floyd/1125.cpp | 30 +++++++++--------- .../T3}/Floyd/1125.drawio | 0 .../{AcWing => AcWing_TiGao/T3}/Floyd/1125.md | 0 .../{AcWing => AcWing_TiGao/T3}/Floyd/343.md | 0 .../T3}/Floyd/343_1.cpp | 0 .../T3}/Floyd/343_2.cpp | 0 .../{AcWing => AcWing_TiGao/T3}/Floyd/344.cpp | 0 .../{AcWing => AcWing_TiGao/T3}/Floyd/344.md | 0 .../{AcWing => AcWing_TiGao/T3}/Floyd/345.cpp | 0 .../T3}/Floyd/345.eddx | Bin .../{AcWing => AcWing_TiGao/T3}/Floyd/345.md | 0 11 files changed, 15 insertions(+), 15 deletions(-) rename TangDou/{AcWing => AcWing_TiGao/T3}/Floyd/1125.cpp (71%) rename TangDou/{AcWing => AcWing_TiGao/T3}/Floyd/1125.drawio (100%) rename TangDou/{AcWing => AcWing_TiGao/T3}/Floyd/1125.md (100%) rename TangDou/{AcWing => AcWing_TiGao/T3}/Floyd/343.md (100%) rename TangDou/{AcWing => AcWing_TiGao/T3}/Floyd/343_1.cpp (100%) rename TangDou/{AcWing => AcWing_TiGao/T3}/Floyd/343_2.cpp (100%) rename TangDou/{AcWing => AcWing_TiGao/T3}/Floyd/344.cpp (100%) rename TangDou/{AcWing => AcWing_TiGao/T3}/Floyd/344.md (100%) rename TangDou/{AcWing => AcWing_TiGao/T3}/Floyd/345.cpp (100%) rename TangDou/{AcWing => AcWing_TiGao/T3}/Floyd/345.eddx (100%) rename TangDou/{AcWing => AcWing_TiGao/T3}/Floyd/345.md (100%) diff --git a/TangDou/AcWing/Floyd/1125.cpp b/TangDou/AcWing_TiGao/T3/Floyd/1125.cpp similarity index 71% rename from TangDou/AcWing/Floyd/1125.cpp rename to TangDou/AcWing_TiGao/T3/Floyd/1125.cpp index bd2dba8..d5dcf74 100644 --- a/TangDou/AcWing/Floyd/1125.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/1125.cpp @@ -6,10 +6,10 @@ typedef pair PII; const int N = 160; const int INF = 0x3f3f3f3f; -PII q[N]; // 每个点的坐标 -char g[N][N]; // 邻接矩阵,记录是否中间有边 -double dist[N][N]; // 每两个牧区之间的距离 -double maxd[N]; // 距离牧区i最远的最短距离是多少 +PII q[N]; // 每个点的坐标 +char g[N][N]; // 邻接矩阵,记录是否中间有边 +double dis[N][N]; // 每两个牧区之间的距离 +double maxd[N]; // 距离牧区i最远的最短距离是多少 // 欧几里得距离 double get(PII a, PII b) { @@ -31,35 +31,35 @@ int main() { // ① 距离只在同一连通块中存在,不同的连通块间的距离是INF // ② 自己与自己的距离是0 // ③ 两个牧区相连,距离=sqrt((x1-x2)^2+(y1-y2)^2) - // 本质: g + q => dist - for (int i = 0; i < n; i++) { + // 本质: g + q => dis + for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { // 1. double数组,在全局变量区,默认值是0 // 2. 当i==j时,自己到自己的距离是0,所以没动作,直接使用默认值,即d[i][i]=0,自己到自己没有距离 // 3. 当g[i][j]=='1'时,说明两者之间存在一条边,距离就是欧几里得距离计算办法 // 4. 否则就是没有路径 if (i == j) - dist[i][j] = 0; + dis[i][j] = 0; else if (g[i][j] == '1') - dist[i][j] = get(q[i], q[j]); - else // 注意:由于dist数组是一个double类型,不能用memset(0x3f)进行初始化正无穷 - dist[i][j] = INF; + dis[i][j] = get(q[i], q[j]); + else // 注意:由于dis数组是一个double类型,不能用memset(0x3f)进行初始化正无穷 + dis[i][j] = INF; } - } // ① Floyd算法 k,i,j // 原始各连通块内的多源最短路径 for (int k = 0; k < n; k++) for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) - dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]); + dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); // ② 未建设两个连通块之间线路前,每个点的最长 最短路径 + // maxd[i]:由i出发,可以走的最远距离 double res1 = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) // 求i到离i(最短路径) 最长距离 - if (dist[i][j] < INF) maxd[i] = max(maxd[i], dist[i][j]); - // res1保持原来(两个牧场中)任意两个牧区间的最大距离(直径) + if (dis[i][j] < INF) maxd[i] = max(maxd[i], dis[i][j]); + // 所有点的最远距离PK,可以获取到最大直径 res1 = max(res1, maxd[i]); } @@ -67,7 +67,7 @@ int main() { double res2 = INF; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) - if (dist[i][j] == INF) // 如果i,j不在同一个连通块内 + if (dis[i][j] == INF) // 如果i,j不在同一个连通块内 // 连接原来不在同一连通块中的两个点后,可以取得的最小直径 res2 = min(res2, maxd[i] + maxd[j] + get(q[i], q[j])); // PK一下 diff --git a/TangDou/AcWing/Floyd/1125.drawio b/TangDou/AcWing_TiGao/T3/Floyd/1125.drawio similarity index 100% rename from TangDou/AcWing/Floyd/1125.drawio rename to TangDou/AcWing_TiGao/T3/Floyd/1125.drawio diff --git a/TangDou/AcWing/Floyd/1125.md b/TangDou/AcWing_TiGao/T3/Floyd/1125.md similarity index 100% rename from TangDou/AcWing/Floyd/1125.md rename to TangDou/AcWing_TiGao/T3/Floyd/1125.md diff --git a/TangDou/AcWing/Floyd/343.md b/TangDou/AcWing_TiGao/T3/Floyd/343.md similarity index 100% rename from TangDou/AcWing/Floyd/343.md rename to TangDou/AcWing_TiGao/T3/Floyd/343.md diff --git a/TangDou/AcWing/Floyd/343_1.cpp b/TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp similarity index 100% rename from TangDou/AcWing/Floyd/343_1.cpp rename to TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp diff --git a/TangDou/AcWing/Floyd/343_2.cpp b/TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp similarity index 100% rename from TangDou/AcWing/Floyd/343_2.cpp rename to TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp diff --git a/TangDou/AcWing/Floyd/344.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344.cpp similarity index 100% rename from TangDou/AcWing/Floyd/344.cpp rename to TangDou/AcWing_TiGao/T3/Floyd/344.cpp diff --git a/TangDou/AcWing/Floyd/344.md b/TangDou/AcWing_TiGao/T3/Floyd/344.md similarity index 100% rename from TangDou/AcWing/Floyd/344.md rename to TangDou/AcWing_TiGao/T3/Floyd/344.md diff --git a/TangDou/AcWing/Floyd/345.cpp b/TangDou/AcWing_TiGao/T3/Floyd/345.cpp similarity index 100% rename from TangDou/AcWing/Floyd/345.cpp rename to TangDou/AcWing_TiGao/T3/Floyd/345.cpp diff --git a/TangDou/AcWing/Floyd/345.eddx b/TangDou/AcWing_TiGao/T3/Floyd/345.eddx similarity index 100% rename from TangDou/AcWing/Floyd/345.eddx rename to TangDou/AcWing_TiGao/T3/Floyd/345.eddx diff --git a/TangDou/AcWing/Floyd/345.md b/TangDou/AcWing_TiGao/T3/Floyd/345.md similarity index 100% rename from TangDou/AcWing/Floyd/345.md rename to TangDou/AcWing_TiGao/T3/Floyd/345.md From c5712548130ef0f3390262d17ffc0106a72962cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 3 Jan 2024 08:09:36 +0800 Subject: [PATCH 42/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/1125.md | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/1125.md b/TangDou/AcWing_TiGao/T3/Floyd/1125.md index 190b864..bbf9310 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/1125.md +++ b/TangDou/AcWing_TiGao/T3/Floyd/1125.md @@ -144,10 +144,10 @@ typedef pair PII; const int N = 160; const int INF = 0x3f3f3f3f; -PII q[N]; // 每个点的坐标 -char g[N][N]; // 邻接矩阵,记录是否中间有边 -double dist[N][N]; // 每两个牧区之间的距离 -double maxd[N]; // 距离牧区i最远的最短距离是多少 +PII q[N]; // 每个点的坐标 +char g[N][N]; // 邻接矩阵,记录是否中间有边 +double dis[N][N]; // 每两个牧区之间的距离 +double maxd[N]; // 距离牧区i最远的最短距离是多少 // 欧几里得距离 double get(PII a, PII b) { @@ -169,35 +169,35 @@ int main() { // ① 距离只在同一连通块中存在,不同的连通块间的距离是INF // ② 自己与自己的距离是0 // ③ 两个牧区相连,距离=sqrt((x1-x2)^2+(y1-y2)^2) - // 本质: g + q => dist - for (int i = 0; i < n; i++) { + // 本质: g + q => dis + for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { // 1. double数组,在全局变量区,默认值是0 // 2. 当i==j时,自己到自己的距离是0,所以没动作,直接使用默认值,即d[i][i]=0,自己到自己没有距离 // 3. 当g[i][j]=='1'时,说明两者之间存在一条边,距离就是欧几里得距离计算办法 // 4. 否则就是没有路径 if (i == j) - dist[i][j] = 0; + dis[i][j] = 0; else if (g[i][j] == '1') - dist[i][j] = get(q[i], q[j]); - else // 注意:由于dist数组是一个double类型,不能用memset(0x3f)进行初始化正无穷 - dist[i][j] = INF; + dis[i][j] = get(q[i], q[j]); + else // 注意:由于dis数组是一个double类型,不能用memset(0x3f)进行初始化正无穷 + dis[i][j] = INF; } - } // ① Floyd算法 k,i,j // 原始各连通块内的多源最短路径 for (int k = 0; k < n; k++) for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) - dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]); + dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); // ② 未建设两个连通块之间线路前,每个点的最长 最短路径 + // maxd[i]:由i出发,可以走的最远距离 double res1 = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) // 求i到离i(最短路径) 最长距离 - if (dist[i][j] < INF) maxd[i] = max(maxd[i], dist[i][j]); - // res1保持原来(两个牧场中)任意两个牧区间的最大距离(直径) + if (dis[i][j] < INF) maxd[i] = max(maxd[i], dis[i][j]); + // 所有点的最远距离PK,可以获取到最大直径 res1 = max(res1, maxd[i]); } @@ -205,7 +205,7 @@ int main() { double res2 = INF; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) - if (dist[i][j] == INF) // 如果i,j不在同一个连通块内 + if (dis[i][j] == INF) // 如果i,j不在同一个连通块内 // 连接原来不在同一连通块中的两个点后,可以取得的最小直径 res2 = min(res2, maxd[i] + maxd[j] + get(q[i], q[j])); // PK一下 From a16f2c7e5c08b3bd1e93c5c05b879714f203fecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 3 Jan 2024 08:23:14 +0800 Subject: [PATCH 43/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/1125.cpp | 20 ++++++++++++-------- TangDou/AcWing_TiGao/T3/Floyd/1125.md | 20 ++++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/1125.cpp b/TangDou/AcWing_TiGao/T3/Floyd/1125.cpp index d5dcf74..36254ae 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/1125.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/1125.cpp @@ -1,15 +1,18 @@ #include using namespace std; + +typedef pair PII; #define x first #define y second -typedef pair PII; + const int N = 160; const int INF = 0x3f3f3f3f; - PII q[N]; // 每个点的坐标 char g[N][N]; // 邻接矩阵,记录是否中间有边 -double dis[N][N]; // 每两个牧区之间的距离 -double maxd[N]; // 距离牧区i最远的最短距离是多少 +double dis[N][N]; // 每两个牧区(点)之间的距离 +double maxd[N]; // maxd[i]:由i点出发,可以到达的最远的最短距离是多少 +// Q:什么是最远的最短距离? +// 答:举个不太恰当的例子,比如A->B->C->D,边权都是1 ,同时存在一条A->D,边权是1。此时,有短的不取长的,所以A->D的距离是1,不是3。 // 欧几里得距离 double get(PII a, PII b) { @@ -25,6 +28,7 @@ int main() { // 邻接矩阵,描述点与点之间的连通关系 // 这个用int还没法读入,因为它的输入是连续的,中间没有空格,讨厌啊~ + // 字符数组与scanf("%s",g[i])相结合,直接写入二维数组g的每一行上,这个技巧是值得我们学习的。 for (int i = 0; i < n; i++) scanf("%s", g[i]); // 遍历行与列,计算出每两个点之间的距离 @@ -53,17 +57,17 @@ int main() { for (int j = 0; j < n; j++) dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); - // ② 未建设两个连通块之间线路前,每个点的最长 最短路径 - // maxd[i]:由i出发,可以走的最远距离 + // ② (1)求出未建设两个连通块之间线路前,所有连通块的直径最大值res1 + // (2)求出未建设两个连通块之间线路前,每个点的可以到达的最远最短距离,下一步做模拟连线时会用到 double res1 = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) // 求i到离i(最短路径) 最长距离 if (dis[i][j] < INF) maxd[i] = max(maxd[i], dis[i][j]); - // 所有点的最远距离PK,可以获取到最大直径 + // 所有点的最远距离PK,获取所有连通块的最大直径 res1 = max(res1, maxd[i]); } - // ③ 连线操作,更新新牧场直径 + // ③ 模拟连线操作,看看这样连线后生成的新牧场直径会不会刷新原来的记录 double res2 = INF; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/1125.md b/TangDou/AcWing_TiGao/T3/Floyd/1125.md index bbf9310..a62d048 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/1125.md +++ b/TangDou/AcWing_TiGao/T3/Floyd/1125.md @@ -138,16 +138,19 @@ maxd[i] + maxd[j] + get(q[i], q[j]) ```cpp {.line-numbers} #include using namespace std; + +typedef pair PII; #define x first #define y second -typedef pair PII; + const int N = 160; const int INF = 0x3f3f3f3f; - PII q[N]; // 每个点的坐标 char g[N][N]; // 邻接矩阵,记录是否中间有边 -double dis[N][N]; // 每两个牧区之间的距离 -double maxd[N]; // 距离牧区i最远的最短距离是多少 +double dis[N][N]; // 每两个牧区(点)之间的距离 +double maxd[N]; // maxd[i]:由i点出发,可以到达的最远的最短距离是多少 +// Q:什么是最远的最短距离? +// 答:举个不太恰当的例子,比如A->B->C->D,边权都是1 ,同时存在一条A->D,边权是1。此时,有短的不取长的,所以A->D的距离是1,不是3。 // 欧几里得距离 double get(PII a, PII b) { @@ -163,6 +166,7 @@ int main() { // 邻接矩阵,描述点与点之间的连通关系 // 这个用int还没法读入,因为它的输入是连续的,中间没有空格,讨厌啊~ + // 字符数组与scanf("%s",g[i])相结合,直接写入二维数组g的每一行上,这个技巧是值得我们学习的。 for (int i = 0; i < n; i++) scanf("%s", g[i]); // 遍历行与列,计算出每两个点之间的距离 @@ -191,17 +195,17 @@ int main() { for (int j = 0; j < n; j++) dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); - // ② 未建设两个连通块之间线路前,每个点的最长 最短路径 - // maxd[i]:由i出发,可以走的最远距离 + // ② (1)求出未建设两个连通块之间线路前,所有连通块的直径最大值res1 + // (2)求出未建设两个连通块之间线路前,每个点的可以到达的最远最短距离,下一步做模拟连线时会用到 double res1 = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) // 求i到离i(最短路径) 最长距离 if (dis[i][j] < INF) maxd[i] = max(maxd[i], dis[i][j]); - // 所有点的最远距离PK,可以获取到最大直径 + // 所有点的最远距离PK,获取所有连通块的最大直径 res1 = max(res1, maxd[i]); } - // ③ 连线操作,更新新牧场直径 + // ③ 模拟连线操作,看看这样连线后生成的新牧场直径会不会刷新原来的记录 double res2 = INF; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) From fde611c4d4665e63074a58fde039472ce7a104dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 3 Jan 2024 09:07:56 +0800 Subject: [PATCH 44/87] 'commit' --- .../T3/Floyd/{343_1.cpp => 343.cpp} | 5 +- TangDou/AcWing_TiGao/T3/Floyd/343.md | 140 +----------------- TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp | 109 -------------- 3 files changed, 11 insertions(+), 243 deletions(-) rename TangDou/AcWing_TiGao/T3/Floyd/{343_1.cpp => 343.cpp} (95%) delete mode 100644 TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp diff --git a/TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp b/TangDou/AcWing_TiGao/T3/Floyd/343.cpp similarity index 95% rename from TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp rename to TangDou/AcWing_TiGao/T3/Floyd/343.cpp index 2f30f0c..9c69f46 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/343.cpp @@ -1,6 +1,7 @@ #include -// Floyd解决传送闭包问题 using namespace std; + +// Floyd解决传送闭包问题 const int N = 27; int n; // n个变量 int m; // m个不等式 @@ -41,7 +42,7 @@ string getorder() { // 升序输出所有变量 int main() { // n个变量,m个不等式 // 当输入一行 0 0 时,表示输入终止 - while (scanf("%d %d", &n, &m), n && m) { + while (cin >> n >> m, n && m) { string S; int k = 3; // 3:不能确定两两之间的关系 memset(f, 0, sizeof f); // 初始化邻接矩阵 diff --git a/TangDou/AcWing_TiGao/T3/Floyd/343.md b/TangDou/AcWing_TiGao/T3/Floyd/343.md index 7305f97..4c3c56e 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/343.md +++ b/TangDou/AcWing_TiGao/T3/Floyd/343.md @@ -96,7 +96,7 @@ Sorted sequence determined after 4 relations: ABCDE. **概念** 给定若干对元素和若干对二元关系,并且关系具有传递性,通过传递性推导出尽量多的元素之间关系的问题被称为 **传递闭包**。 ->**解释**:比如$a < b,b < c$,就可以推导出$a < c$,如果用图形表示出这种大小关系,就是$a$到$b$有一条有向边,$b$到$c$有一条有向边,可以推出$a$可以到达$c$,找出图中各点能够到达点的集合,就 **类似** 于$floyd$算法求图中任意两点间的最短距离。 +>**解释**:比如$a < b,b < c$,就可以推导出$a < c$,如果用图形表示出这种大小关系,就是$a$到$b$有一条有向边 【小的向大的连一条边】,$b$到$c$有一条有向边,可以推出$a$可以到达$c$,找出图中各点能够到达点的集合,**类似** 于$floyd$算法求图中任意两点间的最短距离 【魔改版的$floyd$】。 **模板** ```cpp {.line-numbers} @@ -147,10 +147,10 @@ int check() { return 1; } ``` +* ① 所有的关系都确定,而且没有发生矛盾 +* ② $f[i][i] = 1$ 发生矛盾 +* ③ $f[i][j] = f[j][i] = 0$ 表示$i$与$j$之间的大小关系还没有确定下来,需要继续读取下一对二元关系 -* ① $f[i][i] = 1$ 发生矛盾 -* ② $f[i][j] = f[j][i] = 0$ 表示$i$与$j$之间的大小关系还没有确定下来,需要继续读取下一对二元关系 -* ③ 所有的关系都确定,而且没有发生矛盾 ```cpp {.line-numbers} string getorder(){ @@ -169,8 +169,9 @@ string getorder(){ #### $Code$ ```cpp {.line-numbers} #include -// Floyd解决传送闭包问题 using namespace std; + +// Floyd解决传送闭包问题 const int N = 27; int n; // n个变量 int m; // m个不等式 @@ -211,7 +212,7 @@ string getorder() { // 升序输出所有变量 int main() { // n个变量,m个不等式 // 当输入一行 0 0 时,表示输入终止 - while (scanf("%d %d", &n, &m), n && m) { + while (cin >> n >> m, n && m) { string S; int k = 3; // 3:不能确定两两之间的关系 memset(f, 0, sizeof f); // 初始化邻接矩阵 @@ -242,129 +243,4 @@ int main() { } return 0; } -``` - -### 三、拓扑序解法 -开始想到拓扑排序,但是感觉从前往后枚举一遍会超时,然后看了蓝书$floyd$的做法实现了一遍,感觉还不如直接拓扑。 - -大致是: - -$m$次循环,每次加入一条边到图中,再跑一遍拓扑排序 - -- 排序后,排序数组不为$n$个,则表示有环,矛盾,跳出循环 -- 排序后,排序数组为$n$个,但是在过程中,有$2$个或以上的点在队列中,表示拓扑序并不唯一,那么此时并不能确定所有点的顺序,因此进行下一次循环 -- 排序后,排序数组为$n$个,且在过程中,队列中一直只有一个,拓扑序唯一,输出结果,跳出循环 - -```cpp {.line-numbers} -#include -using namespace std; - -const int N = 30, M = N * N; - -int n, m; -int a[1050], b[1050]; // a[i] q; - bool flag = 0; - - for (int i = 0; i < n; i++) // 枚举每个节点,入度为零的入队列 - if (in[i] == 0) q.push(i); - - while (q.size()) { - /* - 注意:此处需要优先检查是不是有环,即使检查到某个点有多个前序节点,也并不表示它应该返回2,因为此时也可能是一个环! - 因为一旦检查是环,就不必再录入新的大小关系的,是一个截止的标识! - 总结:判断是不是拓扑序不唯一的标准是: - ① 队列节点数量等于n - ② 在过程中,有2个或以上的点在队列中 - - 如果只发现了②就着急返回拓扑序不唯一,就可能会掉入到是环的坑中! - */ - if (q.size() > 1) flag = 1; - - int u = q.front(); - q.pop(); - d[++dl] = u; // 按出队列的顺序来记录由小到大的关系 - for (int i = h[u]; ~i; i = ne[i]) { - int j = e[i]; - if (--in[j] == 0) q.push(j); - } - } - // 有环 - if (dl < n) return 1; - // 不确定 - if (dl == n && flag) return 2; - // 已确定 - return 3; -} - -int main() { - // n个变量,m个不等式,也就是n 个节点,m条边 - while (~scanf("%d%d", &n, &m) && n | m) { - // 多组测试数据,需要初始化 - - // 链式前向星 - memset(h, -1, sizeof h); - idx = 0; - // 入度数组初始化 - memset(ind, 0, sizeof ind); - - // 输入大小关系,'A'->0,...,'Z'->25 - for (int i = 1; i <= m; i++) { - scanf("%s", s); // 通用格式 类似于: B **注**:此题没有给出$M$的数据范围,导致我调试了半个多小时,差评$yxc$ \ No newline at end of file +``` \ No newline at end of file diff --git a/TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp b/TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp deleted file mode 100644 index 0d92f25..0000000 --- a/TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include -using namespace std; - -const int N = 30, M = N * N; - -int n, m; -int a[1050], b[1050]; // a[i] q; - bool flag = 0; - - for (int i = 0; i < n; i++) // 枚举每个节点,入度为零的入队列 - if (in[i] == 0) q.push(i); - - while (q.size()) { - /* - 注意:此处需要优先检查是不是有环,即使检查到某个点有多个前序节点,也并不表示它应该返回2,因为此时也可能是一个环! - 因为一旦检查是环,就不必再录入新的大小关系的,是一个截止的标识! - 总结:判断是不是拓扑序不唯一的标准是: - ① 队列节点数量等于n - ② 在过程中,有2个或以上的点在队列中 - - 如果只发现了②就着急返回拓扑序不唯一,就可能会掉入到是环的坑中! - */ - if (q.size() > 1) flag = 1; - - int u = q.front(); - q.pop(); - d[++dl] = u; // 按出队列的顺序来记录由小到大的关系 - for (int i = h[u]; ~i; i = ne[i]) { - int j = e[i]; - if (--in[j] == 0) q.push(j); - } - } - // 有环 - if (dl < n) return 1; - // 不确定 - if (dl == n && flag) return 2; - // 已确定 - return 3; -} - -int main() { - // n个变量,m个不等式,也就是n 个节点,m条边 - while (~scanf("%d%d", &n, &m) && n | m) { - // 多组测试数据,需要初始化 - - // 链式前向星 - memset(h, -1, sizeof h); - idx = 0; - // 入度数组初始化 - memset(ind, 0, sizeof ind); - - // 输入大小关系,'A'->0,...,'Z'->25 - for (int i = 1; i <= m; i++) { - scanf("%s", s); // 通用格式 类似于: B Date: Wed, 3 Jan 2024 13:09:33 +0800 Subject: [PATCH 45/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/343.cpp | 75 ------------------------ TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp | 77 +++++++++++++++++++++++++ TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp | 74 ++++++++++++++++++++++++ 3 files changed, 151 insertions(+), 75 deletions(-) delete mode 100644 TangDou/AcWing_TiGao/T3/Floyd/343.cpp create mode 100644 TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp create mode 100644 TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp diff --git a/TangDou/AcWing_TiGao/T3/Floyd/343.cpp b/TangDou/AcWing_TiGao/T3/Floyd/343.cpp deleted file mode 100644 index 9c69f46..0000000 --- a/TangDou/AcWing_TiGao/T3/Floyd/343.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -using namespace std; - -// Floyd解决传送闭包问题 -const int N = 27; -int n; // n个变量 -int m; // m个不等式 -int f[N][N]; // 传递闭包结果 - -void floyd() { - for (int k = 0; k < n; k++) - for (int i = 0; i < n; i++) - for (int j = 0; j < n; j++) - f[i][j] |= f[i][k] & f[k][j]; // i可以到达k,k可以到达j,那么i可以到达j -} -// 1:可以确定两两之间的关系,2:矛盾,3:不能确定两两之间的关系 -int check() { - // 如果i> n >> m, n && m) { - string S; - int k = 3; // 3:不能确定两两之间的关系 - memset(f, 0, sizeof f); // 初始化邻接矩阵 - // m条边 - for (int i = 1; i <= m; i++) { - cin >> S; - // 已确定或者出现了矛盾,就没有必要再处理了,但是,还需要耐心的读取完毕,因为可能还有下一轮,不读入完耽误下一轮 - if (k < 3) continue; - // 变量只可能为大写字母A~Z,映射到0~25 - int a = S[0] - 'A', b = S[2] - 'A'; - f[a][b] = 1; // 记录a +using namespace std; + +const int N = 26; + +int n, m; +int g[N][N]; +bool st[N]; + +// 求传递闭包 +void floyd() { + for (int k = 0; k < n; k++) + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + g[i][j] |= g[i][k] && g[k][j]; +} + +int check() { + for (int i = 0; i < n; i++) + if (g[i][i]) return 2; // 矛盾 + + for (int i = 0; i < n; i++) + for (int j = 0; j < i; j++) + if (!g[i][j] && !g[j][i]) // 待继续 + return 0; + + return 1; // 找到顺序 +} + +char get_min() { + for (int i = 0; i < n; i++) + if (!st[i]) { + bool flag = true; + for (int j = 0; j < n; j++) + if (!st[j] && g[j][i]) { // 如果存在j> n >> m, n || m) { + memset(g, 0, sizeof g); // 邻接矩阵 + int type = 0, t; // type: 0=还需要继续给出条件 1=找到了顺序 2=存在冲突 + // t:在第几次输入后找到了顺序,不能中间break,因为那样会造成数据无法完成读入,后续的操作无法进行,只能记录下来当时的i + for (int i = 1; i <= m; i++) { + char s[5]; + cin >> s; + int a = s[0] - 'A', b = s[2] - 'A'; // A->0,B->1,...,Z->25完成映射关系 + + if (!type) { // 如果不存在矛盾,就尝试找出大小的顺序 + g[a][b] = 1; // 有边 + floyd(); // 求传递闭包 + type = check(); // 检查是不是存在矛盾,或者找到了完整的顺序 + if (type) t = i; // 如果找到了顺序,记录是第几次输入后找到顺序 + } + // 即使存在矛盾,也需要继续读入,直到本轮数据读入完成 + } + + if (!type) + puts("Sorted sequence cannot be determined."); + else if (type == 2) + printf("Inconsistency found after %d relations.\n", t); + else { + memset(st, 0, sizeof st); + printf("Sorted sequence determined after %d relations: ", t); + for (int i = 0; i < n; i++) printf("%c", get_min()); + printf(".\n"); + } + } + return 0; +} \ No newline at end of file diff --git a/TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp b/TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp new file mode 100644 index 0000000..4dc24bf --- /dev/null +++ b/TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp @@ -0,0 +1,74 @@ +#include +using namespace std; + +const int N = 26; + +int n, m; +bool d[N][N]; +bool st[N]; + +int check() { + for (int i = 0; i < n; i++) + if (d[i][i]) return 2; + + for (int i = 0; i < n; i++) + for (int j = 0; j < i; j++) + if (!d[i][j] && !d[j][i]) return 0; + + return 1; +} + +char get_min() { + for (int i = 0; i < n; i++) + if (!st[i]) { + bool flag = true; + for (int j = 0; j < n; j++) + if (!st[j] && d[j][i]) { + flag = false; + break; + } + if (flag) { + st[i] = true; + return 'A' + i; + } + } +} + +int main() { + while (cin >> n >> m, n || m) { + memset(d, 0, sizeof d); + + int type = 0, t; + for (int i = 1; i <= m; i++) { + char str[5]; + cin >> str; + int a = str[0] - 'A', b = str[2] - 'A'; + + if (!type) { + d[a][b] = 1; + for (int x = 0; x < n; x++) { + if (d[x][a]) d[x][b] = 1; + if (d[b][x]) d[a][x] = 1; + for (int y = 0; y < n; y++) + if (d[x][a] && d[b][y]) + d[x][y] = 1; + } + type = check(); + if (type) t = i; + } + } + + if (!type) + puts("Sorted sequence cannot be determined."); + else if (type == 2) + printf("Inconsistency found after %d relations.\n", t); + else { + memset(st, 0, sizeof st); + printf("Sorted sequence determined after %d relations: ", t); + for (int i = 0; i < n; i++) printf("%c", get_min()); + printf(".\n"); + } + } + + return 0; +} \ No newline at end of file From 2eeb4fa09b4d2941c5717508449c11d13d7e156b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 3 Jan 2024 13:14:08 +0800 Subject: [PATCH 46/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp | 43 ++++++++++++------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp b/TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp index e2df5ac..7283744 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp @@ -27,20 +27,19 @@ int check() { return 1; // 找到顺序 } -char get_min() { - for (int i = 0; i < n; i++) - if (!st[i]) { - bool flag = true; - for (int j = 0; j < n; j++) - if (!st[j] && g[j][i]) { // 如果存在j> s; int a = s[0] - 'A', b = s[2] - 'A'; // A->0,B->1,...,Z->25完成映射关系 - if (!type) { // 如果不存在矛盾,就尝试找出大小的顺序 - g[a][b] = 1; // 有边 - floyd(); // 求传递闭包 - type = check(); // 检查是不是存在矛盾,或者找到了完整的顺序 - if (type) t = i; // 如果找到了顺序,记录是第几次输入后找到顺序 + if (!type) { // 如果不存在矛盾,就尝试找出大小的顺序 + g[a][b] = 1; // 有边 + floyd(); // 求传递闭包 + type = check(); // 检查是不是存在矛盾,或者找到了完整的顺序 + if (type > 0) t = i; // 如果找到了顺序,或者发现了矛盾,记录是第几次输入后发现的 } // 即使存在矛盾,也需要继续读入,直到本轮数据读入完成 } @@ -67,10 +66,8 @@ int main() { else if (type == 2) printf("Inconsistency found after %d relations.\n", t); else { - memset(st, 0, sizeof st); - printf("Sorted sequence determined after %d relations: ", t); - for (int i = 0; i < n; i++) printf("%c", get_min()); - printf(".\n"); + string ans = getorder(); // 输出升序排列的所有变量 + printf("Sorted sequence determined after %d relations: %s.\n", t, ans.c_str()); } } return 0; From a720cef8e4c42644fcac9c8001c925dbea2cea65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 3 Jan 2024 13:45:17 +0800 Subject: [PATCH 47/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/343.md | 86 +++++++++++++------------ TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp | 7 +- TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp | 54 ++++++++-------- 3 files changed, 76 insertions(+), 71 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/343.md b/TangDou/AcWing_TiGao/T3/Floyd/343.md index 4c3c56e..b8a7eb0 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/343.md +++ b/TangDou/AcWing_TiGao/T3/Floyd/343.md @@ -171,28 +171,30 @@ string getorder(){ #include using namespace std; -// Floyd解决传送闭包问题 -const int N = 27; -int n; // n个变量 -int m; // m个不等式 -int f[N][N]; // 传递闭包结果 +const int N = 26; +int n, m; +int g[N][N]; +bool st[N]; + +// 求传递闭包 void floyd() { for (int k = 0; k < n; k++) for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) - f[i][j] |= f[i][k] & f[k][j]; // i可以到达k,k可以到达j,那么i可以到达j + g[i][j] |= g[i][k] && g[k][j]; } -// 1:可以确定两两之间的关系,2:矛盾,3:不能确定两两之间的关系 + int check() { - // 如果i> n >> m, n && m) { - string S; - int k = 3; // 3:不能确定两两之间的关系 - memset(f, 0, sizeof f); // 初始化邻接矩阵 - // m条边 + while (cin >> n >> m, n || m) { + memset(g, 0, sizeof g); // 邻接矩阵 + int type = 0, t; // type: 0=还需要继续给出条件 1=找到了顺序 2=存在冲突 + // t:在第几次输入后找到了顺序,不能中间break,因为那样会造成数据无法完成读入,后续的操作无法进行,只能记录下来当时的i for (int i = 1; i <= m; i++) { - cin >> S; - // 已确定或者出现了矛盾,就没有必要再处理了,但是,还需要耐心的读取完毕,因为可能还有下一轮,不读入完耽误下一轮 - if (k < 3) continue; - // 变量只可能为大写字母A~Z,映射到0~25 - int a = S[0] - 'A', b = S[2] - 'A'; - f[a][b] = 1; // 记录a> s; + int a = s[0] - 'A', b = s[2] - 'A'; // A->0,B->1,...,Z->25完成映射关系 + + if (!type) { // 如果不存在矛盾,就尝试找出大小的顺序 + g[a][b] = 1; // 有边 + floyd(); // 求传递闭包 + type = check(); // 检查是不是存在矛盾,或者找到了完整的顺序 + if (type > 0) t = i; // 如果找到了顺序,或者发现了矛盾,记录是第几次输入后发现的 } + // 即使存在矛盾,也需要继续读入,直到本轮数据读入完成 + } + + if (!type) + puts("Sorted sequence cannot be determined."); + else if (type == 2) + printf("Inconsistency found after %d relations.\n", t); + else { + string ans = getorder(); // 输出升序排列的所有变量 + printf("Sorted sequence determined after %d relations: %s.\n", t, ans.c_str()); } - // 所有表达式都输入了,仍然定不下来关系 - if (k == 3) printf("Sorted sequence cannot be determined.\n"); } return 0; } diff --git a/TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp b/TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp index 7283744..433d725 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/343_1.cpp @@ -38,8 +38,11 @@ string getorder() { // 升序输出所有变量 // n - cnt - 1 = 6-5-1 = 0,也就是A放在第一个输出的位置上, 之所以再-1,是因为下标从0开始 s[n - cnt - 1] = i + 'A'; } - // 转s字符数组为字符串 - return string(s, s + n); + + // 转s字符数组为字符串 + string res; + for (int i = 0; i < n; i++) res = res + s[i]; + return res; } int main() { diff --git a/TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp b/TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp index 4dc24bf..ee997b3 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp @@ -4,39 +4,41 @@ using namespace std; const int N = 26; int n, m; -bool d[N][N]; +bool g[N][N]; bool st[N]; int check() { for (int i = 0; i < n; i++) - if (d[i][i]) return 2; + if (g[i][i]) return 2; for (int i = 0; i < n; i++) for (int j = 0; j < i; j++) - if (!d[i][j] && !d[j][i]) return 0; + if (!g[i][j] && !g[j][i]) return 0; return 1; } -char get_min() { - for (int i = 0; i < n; i++) - if (!st[i]) { - bool flag = true; - for (int j = 0; j < n; j++) - if (!st[j] && d[j][i]) { - flag = false; - break; - } - if (flag) { - st[i] = true; - return 'A' + i; - } - } +string getorder() { // 升序输出所有变量 + char s[26]; + for (int i = 0; i < n; i++) { + int cnt = 0; + // f[i][j] = 1表示i可以到达j (i< j) + for (int j = 0; j < n; j++) cnt += g[i][j]; // 比i大的有多少个 + // 举个栗子:i=0,表示字符A + // 比如比i大的有5个,共6个字符:ABCDEF + // n - cnt - 1 = 6-5-1 = 0,也就是A放在第一个输出的位置上, 之所以再-1,是因为下标从0开始 + s[n - cnt - 1] = i + 'A'; + } + + // 转s字符数组为字符串 + string res; + for (int i = 0; i < n; i++) res = res + s[i]; + return res; } int main() { while (cin >> n >> m, n || m) { - memset(d, 0, sizeof d); + memset(g, 0, sizeof g); int type = 0, t; for (int i = 1; i <= m; i++) { @@ -45,13 +47,13 @@ int main() { int a = str[0] - 'A', b = str[2] - 'A'; if (!type) { - d[a][b] = 1; + g[a][b] = 1; for (int x = 0; x < n; x++) { - if (d[x][a]) d[x][b] = 1; - if (d[b][x]) d[a][x] = 1; + if (g[x][a]) g[x][b] = 1; + if (g[b][x]) g[a][x] = 1; for (int y = 0; y < n; y++) - if (d[x][a] && d[b][y]) - d[x][y] = 1; + if (g[x][a] && g[b][y]) + g[x][y] = 1; } type = check(); if (type) t = i; @@ -63,10 +65,8 @@ int main() { else if (type == 2) printf("Inconsistency found after %d relations.\n", t); else { - memset(st, 0, sizeof st); - printf("Sorted sequence determined after %d relations: ", t); - for (int i = 0; i < n; i++) printf("%c", get_min()); - printf(".\n"); + string ans = getorder(); // 输出升序排列的所有变量 + printf("Sorted sequence determined after %d relations: %s.\n", t, ans.c_str()); } } From 2e46b8c04bf545bf98f08264949abe70ba836212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 3 Jan 2024 14:17:15 +0800 Subject: [PATCH 48/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/343.eddx | Bin 0 -> 16971 bytes TangDou/AcWing_TiGao/T3/Floyd/343.md | 94 +++++++++++++++++++++++- TangDou/AcWing_TiGao/T3/Floyd/343_2.cpp | 13 ++-- 3 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 TangDou/AcWing_TiGao/T3/Floyd/343.eddx diff --git a/TangDou/AcWing_TiGao/T3/Floyd/343.eddx b/TangDou/AcWing_TiGao/T3/Floyd/343.eddx new file mode 100644 index 0000000000000000000000000000000000000000..303043a76f25ffb84efb1f462212def41f0e2842 GIT binary patch literal 16971 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
i!SecSV&M1|_+eRkZqQ)Eg~u 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 M^EY **解释**:确定所有元素次序后如何判断元素`i`在第几个位置呢?`f[i][j] = 1`表示`i < j`,因此计算下`i`小于元素的个数`cnt`,就可以判定`i`是第`cnt + 1`大的元素了 -#### $Code$ +#### $Code$ $O(N^3)$ ```cpp {.line-numbers} #include using namespace std; @@ -245,4 +245,96 @@ int main() { } return 0; } +``` + +### 三、优化版本 +$O(N^2)$ + +其实,由于每次新增加的一对$(a,b)$,只会更新与$a,b$有边连接的点,其它的无关点是没有影响的,如果加上一对$(a,b)$就去全新计算,无疑是存在浪费的,可以优化的。 + +怎么优化呢?核心思路就是$(a,b)$做为$floyd$算法的中继点即可,其它点不再被遍历做为中继点。 + +说人话就是: +① 遍历所有节点,找出所有小于$a$的节点$x$,那么$x$一定小于$b$。 +② 遍历所有节点,找出所有大于$b$的节点$x$,那么$a$一定小于$x$。 +③ 遍历所有节点,如果$x +using namespace std; + +const int N = 26; + +int n, m; +bool g[N][N]; +bool st[N]; + +int check() { + for (int i = 0; i < n; i++) + if (g[i][i]) return 2; // 矛盾 + + for (int i = 0; i < n; i++) + for (int j = 0; j < i; j++) + if (!g[i][j] && !g[j][i]) // 待继续 + return 0; + + return 1; // 找到顺序 +} +string getorder() { // 升序输出所有变量 + char s[26]; + for (int i = 0; i < n; i++) { + int cnt = 0; + // f[i][j] = 1表示i可以到达j (i< j) + for (int j = 0; j < n; j++) cnt += g[i][j]; // 比i大的有多少个 + // 举个栗子:i=0,表示字符A + // 比如比i大的有5个,共6个字符:ABCDEF + // n - cnt - 1 = 6-5-1 = 0,也就是A放在第一个输出的位置上, 之所以再-1,是因为下标从0开始 + s[n - cnt - 1] = i + 'A'; + } + + // 转s字符数组为字符串 + string res; + for (int i = 0; i < n; i++) res = res + s[i]; + return res; +} + +int main() { + while (cin >> n >> m, n || m) { + memset(g, 0, sizeof g); + + int type = 0, t; + for (int i = 1; i <= m; i++) { + char str[5]; + cin >> str; + int a = str[0] - 'A', b = str[2] - 'A'; + + // a> str; int a = str[0] - 'A', b = str[2] - 'A'; + // a Date: Wed, 3 Jan 2024 14:25:58 +0800 Subject: [PATCH 49/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/344.cpp | 22 +++++++++++----------- TangDou/AcWing_TiGao/T3/Floyd/344.md | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344.cpp index 83e12a6..78ccb7c 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/344.cpp @@ -3,7 +3,7 @@ using namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; -int g[N][N], dist[N][N]; +int g[N][N], dis[N][N]; int path[N], idx; int mid[N][N]; int ans = INF; @@ -19,7 +19,7 @@ void get_path(int i, int j) { int main() { // n个顶点,m条边 - scanf("%d %d", &n, &m); + cin >> n >> m; // 初始化邻接矩阵 memset(g, 0x3f, sizeof g); @@ -27,12 +27,12 @@ int main() { while (m--) { int a, b, c; - scanf("%d %d %d", &a, &b, &c); + cin >> a >> b >> c; g[a][b] = g[b][a] = min(g[a][b], c); // 求最短路之类,(a,b)之间多条边输入只保留最短边 } - // 把原始地图复制出来到生成最短距离dist - memcpy(dist, g, sizeof dist); + // 把原始地图复制出来到生成最短距离dis + memcpy(dis, g, sizeof dis); for (int k = 1; k <= n; k++) { // 枚举每一个引入点k来连接缩短i,j的距离 /* @@ -41,13 +41,13 @@ int main() { 其实循环到n也是可以的,不过当i, j, k中有两个相同时就要continue一下 Q2:为什么非得把DP的这段代码嵌入到Floyd的整体代码中,不能先Floyd后再进行DP吗? - A:是不可以的。因为在进行插入节点号为k时,其实dist[i][j]中记录的是1~k-1插点后的最小距离, + A:是不可以的。因为在进行插入节点号为k时,其实dis[i][j]中记录的是1~k-1插点后的最小距离, 而不是全部插入点后的最短距离。 */ for (int i = 1; i < k; i++) for (int j = i + 1; j < k; j++) - if (g[i][k] + g[k][j] < ans - dist[i][j]) { // 减法防止爆INT - ans = dist[i][j] + g[i][k] + g[k][j]; + if (g[i][k] + g[k][j] < ans - dis[i][j]) { // 减法防止爆INT + ans = dis[i][j] + g[i][k] + g[k][j]; // 找到更小的环,需要记录路径,并且要求: 最小环的所有节点(按顺序输出) // 顺序 // 1. 上面的i,j枚举逻辑是j>i,所以i是第一个 @@ -56,7 +56,7 @@ int main() { // 4. 记录k idx = 0; path[idx++] = i; - get_path(i, j); // i是怎么到达j的?就是问dist[i][j]是怎么获取到的,这是在求最短路径过程中的一个路径记录问题 + get_path(i, j); // i是怎么到达j的?就是问dis[i][j]是怎么获取到的,这是在求最短路径过程中的一个路径记录问题 path[idx++] = j; path[idx++] = k; } @@ -64,8 +64,8 @@ int main() { // 正常floyd for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) - if (dist[i][j] > dist[i][k] + dist[k][j]) { - dist[i][j] = dist[i][k] + dist[k][j]; + if (dis[i][j] > dis[i][k] + dis[k][j]) { + dis[i][j] = dis[i][k] + dis[k][j]; mid[i][j] = k; // 记录路径i->j 是通过k进行转移的 } } diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344.md b/TangDou/AcWing_TiGao/T3/Floyd/344.md index 6384c32..2138e43 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344.md +++ b/TangDou/AcWing_TiGao/T3/Floyd/344.md @@ -40,7 +40,7 @@ $1≤N≤100,1≤M≤10000,1≤l<500$ ![](https://cdn.acwing.com/media/article/image/2021/12/18/85607_ee5522ae60-g.png) -$floyd$是 **插点** 算法,在点$k$被 **插入前** 可计算$i->x>j,x \in [1 \sim k-1]$这样的最短路,当然,也可以不选择任何一个中间点,$dist[i][j]$天生最小。 +$floyd$是 **插点** 算法,在点$k$被 **插入前** 可计算$i \rightarrow x \rightarrow j,x \in [1 \sim k-1]$这样的最短路,当然,也可以不选择任何一个中间点,$dist[i][j]$天生最小。 枚举所有以$k$为环中 **最大节点** 的环即可。 From 96c6bdaa522ad073d1890dbe28ccf2243c9edd40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 3 Jan 2024 14:27:52 +0800 Subject: [PATCH 50/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/344.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344.cpp index 78ccb7c..e4cee81 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/344.cpp @@ -4,7 +4,7 @@ using namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; int g[N][N], dis[N][N]; -int path[N], idx; +vector path; int mid[N][N]; int ans = INF; @@ -13,7 +13,7 @@ void get_path(int i, int j) { int k = mid[i][j]; // 获取中间转移点 if (!k) return; // 如果i,j之间没有中间点,停止 get_path(i, k); // 递归前半段 - path[idx++] = k; // 记录k节点 + path.push_back(k); // 记录k节点 get_path(k, j); // 递归后半段 } @@ -54,11 +54,11 @@ int main() { // 2. i->j 中间的路线不明,需要用get_path进行查询出i->j的最短路径怎么走,当然,也是在 Date: Wed, 3 Jan 2024 14:28:33 +0800 Subject: [PATCH 51/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/344.md | 38 +++++++++++++--------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344.md b/TangDou/AcWing_TiGao/T3/Floyd/344.md index 2138e43..2b8e45b 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344.md +++ b/TangDou/AcWing_TiGao/T3/Floyd/344.md @@ -44,8 +44,6 @@ $floyd$是 **插点** 算法,在点$k$被 **插入前** 可计算$i \rightarro 枚举所有以$k$为环中 **最大节点** 的环即可。 -> **解释**:$k$是从$1\sim n$的,说它是最大节点,是指每次插入的节点号最大,并不表示在环中它一定比$i,j$还大。 - ### 三、$floyd+dp$ ```cpp {.line-numbers} @@ -54,8 +52,8 @@ $floyd$是 **插点** 算法,在点$k$被 **插入前** 可计算$i \rightarro using namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; -int g[N][N], dist[N][N]; -int path[N], idx; +int g[N][N], dis[N][N]; +vector path; int mid[N][N]; int ans = INF; @@ -64,13 +62,13 @@ void get_path(int i, int j) { int k = mid[i][j]; // 获取中间转移点 if (!k) return; // 如果i,j之间没有中间点,停止 get_path(i, k); // 递归前半段 - path[idx++] = k; // 记录k节点 + path.push_back(k); // 记录k节点 get_path(k, j); // 递归后半段 } int main() { // n个顶点,m条边 - scanf("%d %d", &n, &m); + cin >> n >> m; // 初始化邻接矩阵 memset(g, 0x3f, sizeof g); @@ -78,12 +76,12 @@ int main() { while (m--) { int a, b, c; - scanf("%d %d %d", &a, &b, &c); + cin >> a >> b >> c; g[a][b] = g[b][a] = min(g[a][b], c); // 求最短路之类,(a,b)之间多条边输入只保留最短边 } - // 把原始地图复制出来到生成最短距离dist - memcpy(dist, g, sizeof dist); + // 把原始地图复制出来到生成最短距离dis + memcpy(dis, g, sizeof dis); for (int k = 1; k <= n; k++) { // 枚举每一个引入点k来连接缩短i,j的距离 /* @@ -92,31 +90,31 @@ int main() { 其实循环到n也是可以的,不过当i, j, k中有两个相同时就要continue一下 Q2:为什么非得把DP的这段代码嵌入到Floyd的整体代码中,不能先Floyd后再进行DP吗? - A:是不可以的。因为在进行插入节点号为k时,其实dist[i][j]中记录的是1~k-1插点后的最小距离, + A:是不可以的。因为在进行插入节点号为k时,其实dis[i][j]中记录的是1~k-1插点后的最小距离, 而不是全部插入点后的最短距离。 */ for (int i = 1; i < k; i++) for (int j = i + 1; j < k; j++) - if (g[i][k] + g[k][j] < ans - dist[i][j]) { // 减法防止爆INT - ans = dist[i][j] + g[i][k] + g[k][j]; + if (g[i][k] + g[k][j] < ans - dis[i][j]) { // 减法防止爆INT + ans = dis[i][j] + g[i][k] + g[k][j]; // 找到更小的环,需要记录路径,并且要求: 最小环的所有节点(按顺序输出) // 顺序 // 1. 上面的i,j枚举逻辑是j>i,所以i是第一个 // 2. i->j 中间的路线不明,需要用get_path进行查询出i->j的最短路径怎么走,当然,也是在 dist[i][k] + dist[k][j]) { - dist[i][j] = dist[i][k] + dist[k][j]; + if (dis[i][j] > dis[i][k] + dis[k][j]) { + dis[i][j] = dis[i][k] + dis[k][j]; mid[i][j] = k; // 记录路径i->j 是通过k进行转移的 } } @@ -124,7 +122,7 @@ int main() { if (ans == INF) puts("No solution."); else - for (int i = 0; i < idx; i++) cout << path[i] << ' '; + for (int i = 0; i < path.size(); i++) cout << path[i] << ' '; return 0; } From 82a10e5242e2c6291f149a0d0f01d668440240ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 3 Jan 2024 14:29:40 +0800 Subject: [PATCH 52/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/344.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344.md b/TangDou/AcWing_TiGao/T3/Floyd/344.md index 2b8e45b..dfe7733 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344.md +++ b/TangDou/AcWing_TiGao/T3/Floyd/344.md @@ -35,7 +35,7 @@ $1≤N≤100,1≤M≤10000,1≤l<500$ 1 3 5 2 ``` -### 二、$floyd + dp$ 求最小环模板(最少三点) +### 二、算法思路 ![](https://cdn.acwing.com/media/article/image/2021/12/18/85607_ee5522ae60-g.png) @@ -45,7 +45,7 @@ $floyd$是 **插点** 算法,在点$k$被 **插入前** 可计算$i \rightarro 枚举所有以$k$为环中 **最大节点** 的环即可。 -### 三、$floyd+dp$ +#### $Code$ ```cpp {.line-numbers} #include From 3ad846a3121ce6f7f7ba39d0fc63c269a5096298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 3 Jan 2024 16:57:41 +0800 Subject: [PATCH 53/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/344.md | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344.md b/TangDou/AcWing_TiGao/T3/Floyd/344.md index dfe7733..dda5396 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344.md +++ b/TangDou/AcWing_TiGao/T3/Floyd/344.md @@ -37,13 +37,33 @@ $1≤N≤100,1≤M≤10000,1≤l<500$ ### 二、算法思路 -![](https://cdn.acwing.com/media/article/image/2021/12/18/85607_ee5522ae60-g.png) +![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401031636536.png) +最优化问题,可以从集合角度来思考,从集合角度来思考的一个好处就是:不容易丢东西。 -$floyd$是 **插点** 算法,在点$k$被 **插入前** 可计算$i \rightarrow x \rightarrow j,x \in [1 \sim k-1]$这样的最短路,当然,也可以不选择任何一个中间点,$dist[i][j]$天生最小。 +![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401031639098.png) -枚举所有以$k$为环中 **最大节点** 的环即可。 +按环上编号最大点的编号为分类依据,分完类之后,只需要分别求一个每一类的最小值,然后$PK$一下求$min$所有最小值就是答案。 +每一类的最小值怎么求呢?我们来加快一下$floyd$的过程: + +```cpp {.line-numbers} +for(int k=1;k<=n;k++) //K是要插入的点,dis[i][j]数组相当是知道了i~j的只经过1~k-1这些点的最小路径 + //此时在这个地方可以求第k类。从某个点连接到k + for(int i=1;i<=n;i++) + for(int j=1;j<=n;j++){ + + } +``` +![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401031648374.png) + +枚举一下所有的点对(i,j),固定了(i,j)之后,那么$i-k$,$k-j$的长度都是固定的。 + +![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401031650078.png) + +本题还有一个难点,就是$floyd$需要记录方案,其实就是求一下$d[i][j]$是由哪个中间点转移过来的。 + +k的含义:不算i,j的情况下,中间点里的最大值。 #### $Code$ ```cpp {.line-numbers} From f7bc8902db98c30654cac44f17481c524f74cb9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 3 Jan 2024 19:49:47 +0800 Subject: [PATCH 54/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/344.cpp | 12 ++--------- TangDou/AcWing_TiGao/T3/Floyd/344.md | 31 +++++++++++++++++++++------ 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344.cpp index e4cee81..e34e3ef 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/344.cpp @@ -34,16 +34,8 @@ int main() { // 把原始地图复制出来到生成最短距离dis memcpy(dis, g, sizeof dis); - for (int k = 1; k <= n; k++) { // 枚举每一个引入点k来连接缩短i,j的距离 - /* - Q1:为什么循环的时候i和j都需要小于k? - A:为了避免经过相同的点,比如i == k时,三个点就变成两个点了。 - 其实循环到n也是可以的,不过当i, j, k中有两个相同时就要continue一下 - - Q2:为什么非得把DP的这段代码嵌入到Floyd的整体代码中,不能先Floyd后再进行DP吗? - A:是不可以的。因为在进行插入节点号为k时,其实dis[i][j]中记录的是1~k-1插点后的最小距离, - 而不是全部插入点后的最短距离。 - */ + for (int k = 1; k <= n; k++) { + // DP for (int i = 1; i < k; i++) for (int j = i + 1; j < k; j++) if (g[i][k] + g[k][j] < ans - dis[i][j]) { // 减法防止爆INT diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344.md b/TangDou/AcWing_TiGao/T3/Floyd/344.md index dda5396..18ad9d4 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344.md +++ b/TangDou/AcWing_TiGao/T3/Floyd/344.md @@ -37,33 +37,52 @@ $1≤N≤100,1≤M≤10000,1≤l<500$ ### 二、算法思路 +> 环上的节点不重复,并且环上的边的长度之和最小。 +**解释**: ![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401031636536.png) +> 如果存在一个环,则上图中$k$出现多次,那么,如果去掉$k$身上的那个环,$a \rightarrow k \rightarrow b \rightarrow a $这个环的长度肯定是最小的。 最优化问题,可以从集合角度来思考,从集合角度来思考的一个好处就是:不容易丢东西。 ![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401031639098.png) -按环上编号最大点的编号为分类依据,分完类之后,只需要分别求一个每一类的最小值,然后$PK$一下求$min$所有最小值就是答案。 +按 **环上编号最大点的编号** 为分类依据,分完类之后,只需要分别求一个每一类的最小值,然后求$min$所有最小值就是答案。 -每一类的最小值怎么求呢?我们来加快一下$floyd$的过程: +每一类的最小值怎么求呢?我们来回顾一下$floyd$的过程: ```cpp {.line-numbers} for(int k=1;k<=n;k++) //K是要插入的点,dis[i][j]数组相当是知道了i~j的只经过1~k-1这些点的最小路径 //此时在这个地方可以求第k类。从某个点连接到k for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ - + ... } ``` ![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401031648374.png) -枚举一下所有的点对(i,j),固定了(i,j)之后,那么$i-k$,$k-j$的长度都是固定的。 +枚举一下所有的点对$(i,j)$,固定了$(i,j)$之后,那么$i \rightarrow k$,$k \rightarrow j$的长度都是固定的。 +而左边那个弧的长度,就是$i \rightarrow j$在只有$1 \sim k-1$号点帮助下可以取得的最短距离,而这个距离恰好被保存在 **当前** 的$dis[i][j]$中。 + +也就是说,在正常进行$floyd$算法的第一层 +```cpp {.line-numbers} +for (int k = 1; k <= n ; k++){ + //这里需要加上一些DP的动作,利用floyd进行dp转移 + for (int i = 1; i < k; i++) + for (int j = i + 1; j < k; j++) + if (g[i][k] + g[k][j] < ans - dis[i][j]) // 减法防止爆INT + ans = dis[i][j] + g[i][k] + g[k][j]; + + for (int i = 1; i <= n; i++) + for (int j = 1;j <=n; j++){ + .... + } +} +``` ![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401031650078.png) 本题还有一个难点,就是$floyd$需要记录方案,其实就是求一下$d[i][j]$是由哪个中间点转移过来的。 - -k的含义:不算i,j的情况下,中间点里的最大值。 +**** #### $Code$ ```cpp {.line-numbers} From b67b74ebf36db7b3075b39c1cdab967b6430add4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 3 Jan 2024 19:55:56 +0800 Subject: [PATCH 55/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/344.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344.cpp index e34e3ef..cc24cc7 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/344.cpp @@ -40,15 +40,11 @@ int main() { for (int j = i + 1; j < k; j++) if (g[i][k] + g[k][j] < ans - dis[i][j]) { // 减法防止爆INT ans = dis[i][j] + g[i][k] + g[k][j]; - // 找到更小的环,需要记录路径,并且要求: 最小环的所有节点(按顺序输出) - // 顺序 - // 1. 上面的i,j枚举逻辑是j>i,所以i是第一个 - // 2. i->j 中间的路线不明,需要用get_path进行查询出i->j的最短路径怎么走,当然,也是在 Date: Thu, 4 Jan 2024 13:06:07 +0800 Subject: [PATCH 56/87] 'commit' --- TangDou/Topic/【Floyd专题】.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 TangDou/Topic/【Floyd专题】.md diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md new file mode 100644 index 0000000..317648c --- /dev/null +++ b/TangDou/Topic/【Floyd专题】.md @@ -0,0 +1,7 @@ +## 图论-多源最短路径($Floyd$算法) + +### 一、$Floyd$ +$Floyd$算法是一次性求所有结点之间的最短距离,能处理负权边的图,程序比暴力的$DFS$更简单,但是复杂度是$O(n^3)$,只适合 $n < 200$的情况。 +$Floyd$运用了 **动态规划** 的思想,求 $i 、 j$两点的最短距离,可分两种情况考虑,即经过图中某个点 $k$的路径和不经过点 $k$ 的路径,**取两者中的最短路径**。 + + From df296722680b4da549bfccd52658846c665165ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 4 Jan 2024 14:53:06 +0800 Subject: [PATCH 57/87] 'commit' --- TangDou/Topic/POJ3259.cpp | 50 ++++++++++++++++++++++++++++++ TangDou/Topic/【Floyd专题】.md | 31 ++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 TangDou/Topic/POJ3259.cpp diff --git a/TangDou/Topic/POJ3259.cpp b/TangDou/Topic/POJ3259.cpp new file mode 100644 index 0000000..f2cd9a1 --- /dev/null +++ b/TangDou/Topic/POJ3259.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include +using namespace std; +const int INF = 0x3f3f3f3f; + +const int N = 502; +int n, m, w; +int g[N][N]; + +bool floyd() { + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + if (g[i][k] != INF) { + for (int j = 1; j <= n; j++) + if (g[i][j] > g[i][k] + g[k][j]) + g[i][j] = g[i][k] + g[k][j]; + if (g[i][i] < 0) return true; + } + return false; +} +int main() { + int T; + cin >> T; + while (T--) { + cin >> n >> m >> w; + memset(g, INF, sizeof g); // 初始化邻接矩阵 + + // 双向正值边 + while (m--) { + int a, b, c; + cin >> a >> b >> c; + // 注意坑:重边 + g[a][b] = g[b][a] = min(c, g[a][b]); + } + // 单向负值边 + while (w--) { + int a, b, c; + cin >> a >> b >> c; + g[a][b] = -c; + } + + if (floyd()) + puts("YES"); + else + puts("NO"); + } + return 0; +} \ No newline at end of file diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 317648c..837022c 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -5,3 +5,34 @@ $Floyd$算法是一次性求所有结点之间的最短距离,能处理负权 $Floyd$运用了 **动态规划** 的思想,求 $i 、 j$两点的最短距离,可分两种情况考虑,即经过图中某个点 $k$的路径和不经过点 $k$ 的路径,**取两者中的最短路径**。 +- 判断负圈 +眼尖的人儿可能发现邻接矩阵 $mp$ 中, $mp[i][i]$并没有赋初值$0$,而是 $inf$。并且计算后 $mp[i][i]$的值也不是 $0$,而是 $mp[i][i]=mp[i][u]+……+mp[v][i]$,即从外面绕一圈回来的最短路径,而这正 **用于判断负圈**,即 $mp[i][i]<0$。 + +相关变形结合题目讲,如:负圈、打印路径、最小环、传递闭包 + +记录坑点:**重复边**,保留最小的那个。 + + +### 二、模板 +```cpp {.line-numbers} +void floyd() { + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + if (g[i][k] != inf) //优化 + for (int j = 1; j <= n; j++) + if (g[i][j] > g[i][k] + g[k][j]) + g[i][j] = g[i][k] + g[k][j]; +} +``` + +### 三、例题 + +#### [$POJ-3259$ $Wormholes$ 负圈](https://link.juejin.cn/?target=https%3A%2F%2Fvjudge.net%2Fproblem%2FPOJ-3259) + +**分析**: +给定若干双向正值边和单向负值边,问是否存在负圈(使其时光倒流回到原点)。 +所以在第二重循环,求完第$i$个结点后判断。 + + + +https://juejin.cn/post/6935691567696969764 \ No newline at end of file From 1e1d3fd916e3313d3d04ccaebf516061d638340d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 4 Jan 2024 15:09:07 +0800 Subject: [PATCH 58/87] 'commit' --- TangDou/Topic/HDU1385.cpp | 56 ++++++++++++++++++++ TangDou/Topic/POJ3259.cpp | 7 +-- TangDou/Topic/【Floyd专题】.md | 85 ++++++++++++++++++++++++++++-- 3 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 TangDou/Topic/HDU1385.cpp diff --git a/TangDou/Topic/HDU1385.cpp b/TangDou/Topic/HDU1385.cpp new file mode 100644 index 0000000..99aa448 --- /dev/null +++ b/TangDou/Topic/HDU1385.cpp @@ -0,0 +1,56 @@ +#include +using namespace std; +const int INF = 0x3f3f3f3f; + +const int N = 1003; +int mp[N][N], path[N][N]; // 邻接矩阵、路径 +int n, x, y, u, v, cost[N]; // 额外费用 +void floyd() { + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + if (mp[i][k] != INF) + for (int j = 1; j <= n; j++) { + if (mp[i][j] > mp[i][k] + mp[k][j] + cost[k]) { + mp[i][j] = mp[i][k] + mp[k][j] + cost[k]; + path[i][j] = path[i][k]; + } + if (mp[i][j] == mp[i][k] + mp[k][j] + cost[k]) { + if (path[i][j] > path[i][k]) // 字典序 + path[i][j] = path[i][k]; + } + } +} +int main() { + while (cin >> n && n) { + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + path[i][j] = j; + cin >> mp[i][j]; + if (mp[i][j] == -1) mp[i][j] = INF; + } + } + for (int i = 1; i <= n; i++) cin >> cost[i]; + floyd(); + + while (cin >> x >> y) { + if (x == -1 && y == -1) break; + u = x, v = y; + printf("From %d to %d :\n", x, y); + printf("Path: %d", x); + + while (x != y) { + printf("-->%d", path[x][y]); + x = path[x][y]; + } + + printf("\n"); + + if (mp[u][v] < INF) + printf("Total cost : %d\n", mp[u][v]); + else + printf("-1\n"); + printf("\n"); + } + } + return 0; +} \ No newline at end of file diff --git a/TangDou/Topic/POJ3259.cpp b/TangDou/Topic/POJ3259.cpp index f2cd9a1..da42f28 100644 --- a/TangDou/Topic/POJ3259.cpp +++ b/TangDou/Topic/POJ3259.cpp @@ -9,14 +9,15 @@ const int N = 502; int n, m, w; int g[N][N]; +// floyd判断是否存在负圈 bool floyd() { for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) - if (g[i][k] != INF) { + if (g[i][k] != INF) { // 优化 for (int j = 1; j <= n; j++) if (g[i][j] > g[i][k] + g[k][j]) g[i][j] = g[i][k] + g[k][j]; - if (g[i][i] < 0) return true; + if (g[i][i] < 0) return true; // 发现负圈 } return false; } @@ -38,7 +39,7 @@ int main() { while (w--) { int a, b, c; cin >> a >> b >> c; - g[a][b] = -c; + g[a][b] = -c; // 负值边 } if (floyd()) diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 837022c..7a0502f 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -27,11 +27,88 @@ void floyd() { ### 三、例题 -#### [$POJ-3259$ $Wormholes$ 负圈](https://link.juejin.cn/?target=https%3A%2F%2Fvjudge.net%2Fproblem%2FPOJ-3259) +#### [$POJ-3259$ $Wormholes$](https://link.juejin.cn/?target=https%3A%2F%2Fvjudge.net%2Fproblem%2FPOJ-3259) + +**类型** +判负环 + +**题意** +- 正常路是$m$条双向正权边 +- 虫洞是$w$条单向负权边 +- 题目让判断是否有负权回路 + +**办法** +利用$Floyd$找两点间花费的最短时间,判断从起始位置到起始位置的最短时间是否为负值(判断负权环),若为负值,说明他通过虫洞回到起始位置时比自己最初离开起始位置的时间早。 + +**代码实现**: +在第二重循环,求完第$i$个结点后判断。$i$到$i$之间的最短距离是一个负值,说明存在一个经过它的负环。 + +```cpp {.line-numbers} +#include +#include +#include +#include +using namespace std; +const int INF = 0x3f3f3f3f; + +const int N = 502; +int n, m, w; +int g[N][N]; + +// floyd判断是否存在负圈 +bool floyd() { + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + if (g[i][k] != INF) { // 优化 + for (int j = 1; j <= n; j++) + if (g[i][j] > g[i][k] + g[k][j]) + g[i][j] = g[i][k] + g[k][j]; + if (g[i][i] < 0) return true; // 发现负圈 + } + return false; +} +int main() { + int T; + cin >> T; + while (T--) { + cin >> n >> m >> w; + memset(g, INF, sizeof g); // 初始化邻接矩阵 + + // 双向正值边 + while (m--) { + int a, b, c; + cin >> a >> b >> c; + // 注意坑:重边 + g[a][b] = g[b][a] = min(c, g[a][b]); + } + // 单向负值边 + while (w--) { + int a, b, c; + cin >> a >> b >> c; + g[a][b] = -c; // 负值边 + } + + if (floyd()) + puts("YES"); + else + puts("NO"); + } + return 0; +} +``` + +#### [$HDU-1385$ $Minimum$ $Transport$ $Cost$](http://acm.hdu.edu.cn/showproblem.php?pid=1385) + +**类型** +打印路径 + +**题意** +给你所有城市到其他城市的道路成本和经过每个城市的城市税,给你很多组城市,要求你找出每组城市间的最低运输成本并且输出路径,如果有多条路径则输出字典序最小的那条路径。**注意**,起点城市和终点城市不需要收城市税。 + +**分析** +输出路径,多个答案则输出字典序最小的,无法到达输出$-1$。 +读入邻接表, $cost[]$记录每个城市额外费用, $path[][]$记录路径,比如 $path[i][j]=k$ 表示 $i$到 $j$的路径是 $i$先到 $k$,再从 $k$到 $j$,$floyd()$里维护即可。然后处理下输出(比较恶心)。 -**分析**: -给定若干双向正值边和单向负值边,问是否存在负圈(使其时光倒流回到原点)。 -所以在第二重循环,求完第$i$个结点后判断。 From 3ea6c1b841e06551c10eebb0d50ec68558dbd6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 4 Jan 2024 15:30:58 +0800 Subject: [PATCH 59/87] 'commit' --- TangDou/Topic/HDU1385.cpp | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/TangDou/Topic/HDU1385.cpp b/TangDou/Topic/HDU1385.cpp index 99aa448..79ae0e9 100644 --- a/TangDou/Topic/HDU1385.cpp +++ b/TangDou/Topic/HDU1385.cpp @@ -3,18 +3,18 @@ using namespace std; const int INF = 0x3f3f3f3f; const int N = 1003; -int mp[N][N], path[N][N]; // 邻接矩阵、路径 -int n, x, y, u, v, cost[N]; // 额外费用 +int g[N][N], path[N][N]; // 邻接矩阵、路径 +int n, x, y, cost[N]; // 额外费用 void floyd() { for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) - if (mp[i][k] != INF) + if (g[i][k] != INF) for (int j = 1; j <= n; j++) { - if (mp[i][j] > mp[i][k] + mp[k][j] + cost[k]) { - mp[i][j] = mp[i][k] + mp[k][j] + cost[k]; + if (g[i][j] > g[i][k] + g[k][j] + cost[k]) { + g[i][j] = g[i][k] + g[k][j] + cost[k]; path[i][j] = path[i][k]; } - if (mp[i][j] == mp[i][k] + mp[k][j] + cost[k]) { + if (g[i][j] == g[i][k] + g[k][j] + cost[k]) { if (path[i][j] > path[i][k]) // 字典序 path[i][j] = path[i][k]; } @@ -25,8 +25,8 @@ int main() { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { path[i][j] = j; - cin >> mp[i][j]; - if (mp[i][j] == -1) mp[i][j] = INF; + cin >> g[i][j]; + if (g[i][j] == -1) g[i][j] = INF; } } for (int i = 1; i <= n; i++) cin >> cost[i]; @@ -34,22 +34,21 @@ int main() { while (cin >> x >> y) { if (x == -1 && y == -1) break; - u = x, v = y; printf("From %d to %d :\n", x, y); printf("Path: %d", x); - + int u = x, v = y; while (x != y) { printf("-->%d", path[x][y]); x = path[x][y]; } - printf("\n"); + puts(""); - if (mp[u][v] < INF) - printf("Total cost : %d\n", mp[u][v]); + if (g[u][v] < INF) + printf("Total cost : %d\n", g[u][v]); else - printf("-1\n"); - printf("\n"); + puts("-1"); + puts(""); } } return 0; From 2e000d82a66e18cd38d54e44d2591ccf272a9b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 4 Jan 2024 15:33:59 +0800 Subject: [PATCH 60/87] 'commit' --- TangDou/Topic/HDU1385.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/TangDou/Topic/HDU1385.cpp b/TangDou/Topic/HDU1385.cpp index 79ae0e9..be7581b 100644 --- a/TangDou/Topic/HDU1385.cpp +++ b/TangDou/Topic/HDU1385.cpp @@ -4,17 +4,17 @@ const int INF = 0x3f3f3f3f; const int N = 1003; int g[N][N], path[N][N]; // 邻接矩阵、路径 -int n, x, y, cost[N]; // 额外费用 +int n, x, y, w[N]; // 额外费用 void floyd() { for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) if (g[i][k] != INF) for (int j = 1; j <= n; j++) { - if (g[i][j] > g[i][k] + g[k][j] + cost[k]) { - g[i][j] = g[i][k] + g[k][j] + cost[k]; + if (g[i][j] > g[i][k] + g[k][j] + w[k]) { + g[i][j] = g[i][k] + g[k][j] + w[k]; path[i][j] = path[i][k]; } - if (g[i][j] == g[i][k] + g[k][j] + cost[k]) { + if (g[i][j] == g[i][k] + g[k][j] + w[k]) { if (path[i][j] > path[i][k]) // 字典序 path[i][j] = path[i][k]; } @@ -24,12 +24,12 @@ int main() { while (cin >> n && n) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { - path[i][j] = j; + path[i][j] = j; // 路径初始化,如果i->j有边,则记录path[i][j]=j,就是记录i->j的中间节点 cin >> g[i][j]; if (g[i][j] == -1) g[i][j] = INF; } } - for (int i = 1; i <= n; i++) cin >> cost[i]; + for (int i = 1; i <= n; i++) cin >> w[i]; floyd(); while (cin >> x >> y) { @@ -37,13 +37,13 @@ int main() { printf("From %d to %d :\n", x, y); printf("Path: %d", x); int u = x, v = y; + // 用循环打印路径 while (x != y) { printf("-->%d", path[x][y]); x = path[x][y]; } puts(""); - if (g[u][v] < INF) printf("Total cost : %d\n", g[u][v]); else From d68d2881bab97bc9580334d6dfd75b4436eb0967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 4 Jan 2024 15:48:28 +0800 Subject: [PATCH 61/87] 'commit' --- TangDou/Topic/HDU1385.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/TangDou/Topic/HDU1385.cpp b/TangDou/Topic/HDU1385.cpp index be7581b..93e6bac 100644 --- a/TangDou/Topic/HDU1385.cpp +++ b/TangDou/Topic/HDU1385.cpp @@ -3,20 +3,22 @@ using namespace std; const int INF = 0x3f3f3f3f; const int N = 1003; -int g[N][N], path[N][N]; // 邻接矩阵、路径 -int n, x, y, w[N]; // 额外费用 +int g[N][N]; // 邻接矩阵 +int n; // n个点 +int w[N]; // 额外费用 +int path[N][N]; // 路径 void floyd() { for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) - if (g[i][k] != INF) + if (g[i][k] != INF) // floyd优化 for (int j = 1; j <= n; j++) { - if (g[i][j] > g[i][k] + g[k][j] + w[k]) { - g[i][j] = g[i][k] + g[k][j] + w[k]; + if (g[i][j] > g[i][k] + g[k][j] + w[k]) { // w[k]:点权 + g[i][j] = g[i][k] + g[k][j] + w[k]; // k的加入,使得i->j的路径变短 path[i][j] = path[i][k]; } - if (g[i][j] == g[i][k] + g[k][j] + w[k]) { - if (path[i][j] > path[i][k]) // 字典序 - path[i][j] = path[i][k]; + + if (g[i][j] == g[i][k] + g[k][j] + w[k]) { // 如果存在多条最短路径,也就是,除了k还有其它k1,k2使得i->j距离一样小 + if (path[i][j] > path[i][k]) path[i][j] = path[i][k]; // 字典序,谁更小就留下谁 } } } @@ -31,13 +33,16 @@ int main() { } for (int i = 1; i <= n; i++) cin >> w[i]; floyd(); - + int x, y; while (cin >> x >> y) { if (x == -1 && y == -1) break; printf("From %d to %d :\n", x, y); printf("Path: %d", x); int u = x, v = y; - // 用循环打印路径 + // 理解路径思路: + // (1) 从起点x出发,用循环打印路径,最后一个打印的肯定是y + // (2) 从起点x出发,第二个点应该是离x最近的,并且是最短路径上的那个点,这个点就是path[x][y]! + // path[x][y]:从起点x出发,到终点y有多条最短路径,我们选择字典序最小的那条最短路径,然后path[x][y]就是从x出发,离x最近的这条最短路径上的点。 while (x != y) { printf("-->%d", path[x][y]); x = path[x][y]; From b11682134d2856fcd52c4d281a5e5cce78c8ab36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 4 Jan 2024 15:50:38 +0800 Subject: [PATCH 62/87] 'commit' --- TangDou/Topic/HDU1385.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/TangDou/Topic/HDU1385.cpp b/TangDou/Topic/HDU1385.cpp index 93e6bac..45acc2e 100644 --- a/TangDou/Topic/HDU1385.cpp +++ b/TangDou/Topic/HDU1385.cpp @@ -31,8 +31,11 @@ int main() { if (g[i][j] == -1) g[i][j] = INF; } } - for (int i = 1; i <= n; i++) cin >> w[i]; + for (int i = 1; i <= n; i++) cin >> w[i]; // 读入点权 + // 多源最短路径 floyd(); + + // 处理询问 int x, y; while (cin >> x >> y) { if (x == -1 && y == -1) break; @@ -44,8 +47,8 @@ int main() { // (2) 从起点x出发,第二个点应该是离x最近的,并且是最短路径上的那个点,这个点就是path[x][y]! // path[x][y]:从起点x出发,到终点y有多条最短路径,我们选择字典序最小的那条最短路径,然后path[x][y]就是从x出发,离x最近的这条最短路径上的点。 while (x != y) { - printf("-->%d", path[x][y]); - x = path[x][y]; + printf("-->%d", path[x][y]); // 输出距离x最近的那个点 + x = path[x][y]; // 更换x概念,向y逼近,让循环跑起来 } puts(""); From ffcaf8681b2218cb0ae37ec48183a97c617db05c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 4 Jan 2024 15:51:30 +0800 Subject: [PATCH 63/87] 'commit' --- TangDou/Topic/HDU1385.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TangDou/Topic/HDU1385.cpp b/TangDou/Topic/HDU1385.cpp index 45acc2e..94c12d8 100644 --- a/TangDou/Topic/HDU1385.cpp +++ b/TangDou/Topic/HDU1385.cpp @@ -26,7 +26,7 @@ int main() { while (cin >> n && n) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { - path[i][j] = j; // 路径初始化,如果i->j有边,则记录path[i][j]=j,就是记录i->j的中间节点 + path[i][j] = j; // 路径初始化,记录整条路径上,离i节点最近的,最短路径上的下一个点,只有i->j时,下一个点可不就是j cin >> g[i][j]; if (g[i][j] == -1) g[i][j] = INF; } From 303d028aaddb094320758b3108efbffeb5d48a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 4 Jan 2024 16:02:54 +0800 Subject: [PATCH 64/87] 'commit' --- TangDou/Topic/HDU1385.cpp | 14 +++--- TangDou/Topic/【Floyd专题】.md | 74 +++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/TangDou/Topic/HDU1385.cpp b/TangDou/Topic/HDU1385.cpp index 94c12d8..b0440f2 100644 --- a/TangDou/Topic/HDU1385.cpp +++ b/TangDou/Topic/HDU1385.cpp @@ -6,7 +6,8 @@ const int N = 1003; int g[N][N]; // 邻接矩阵 int n; // n个点 int w[N]; // 额外费用 -int path[N][N]; // 路径 +int path[N][N]; // i->j 可能存在多条路线,我要找最短的。如果有多条最短的,我要字典序最小的。现在路线唯一了吧!比如这条路线最终是 +// i->a->b->c->d->j,则path[i][j]=a,也就是第一个后继节点。 void floyd() { for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) @@ -14,9 +15,9 @@ void floyd() { for (int j = 1; j <= n; j++) { if (g[i][j] > g[i][k] + g[k][j] + w[k]) { // w[k]:点权 g[i][j] = g[i][k] + g[k][j] + w[k]; // k的加入,使得i->j的路径变短 - path[i][j] = path[i][k]; + path[i][j] = path[i][k]; // 如果i->k->j使得i->j更近,那么根据定义path[i][j]就是这条最短路径中距离i最近的那个点,而这个点由于是出现在i->k的必经之路上,而且是i->k的首席弟子,所以,也必然是i->j的首席弟子。 } - + // 处理字典序 if (g[i][j] == g[i][k] + g[k][j] + w[k]) { // 如果存在多条最短路径,也就是,除了k还有其它k1,k2使得i->j距离一样小 if (path[i][j] > path[i][k]) path[i][j] = path[i][k]; // 字典序,谁更小就留下谁 } @@ -26,12 +27,13 @@ int main() { while (cin >> n && n) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { - path[i][j] = j; // 路径初始化,记录整条路径上,离i节点最近的,最短路径上的下一个点,只有i->j时,下一个点可不就是j - cin >> g[i][j]; - if (g[i][j] == -1) g[i][j] = INF; + path[i][j] = j; // 路径初始化,记录整条路径上,离i节点最近的,最短路径上的下一个点,只有i->j时,下一个点可不就是j + cin >> g[i][j]; // 不管是不是有边,都先录进来 + if (g[i][j] == -1) g[i][j] = INF; // 如果题目中给出的是无边,那么设置为正无穷。此时,有些记录的path[i][j]就是没用的,但没事,后面会被其它代码替换掉path[i][j]。 } } for (int i = 1; i <= n; i++) cin >> w[i]; // 读入点权 + // 多源最短路径 floyd(); diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 7a0502f..3b0b46b 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -103,13 +103,83 @@ int main() { 打印路径 **题意** -给你所有城市到其他城市的道路成本和经过每个城市的城市税,给你很多组城市,要求你找出每组城市间的最低运输成本并且输出路径,如果有多条路径则输出字典序最小的那条路径。**注意**,起点城市和终点城市不需要收城市税。 +给你所有城市到其他城市的道路成本和经过每个城市的城市税,给你很多组城市,要求你找出每组城市间的最低运输成本并且输出路径,**如果有多条路径则输出字典序最小的那条路径**。 **注意**,起点城市和终点城市不需要收城市税(中间点才收税,也就是插值的$k$收税)。 **分析** 输出路径,多个答案则输出字典序最小的,无法到达输出$-1$。 -读入邻接表, $cost[]$记录每个城市额外费用, $path[][]$记录路径,比如 $path[i][j]=k$ 表示 $i$到 $j$的路径是 $i$先到 $k$,再从 $k$到 $j$,$floyd()$里维护即可。然后处理下输出(比较恶心)。 +读入邻接表, $w[]$记录每个城市额外费用, $path[][]$记录路径,$floyd()$里维护即可。然后处理下输出(比较恶心)。 +> **解释**:`int path[N][N]; ` +$i \rightarrow j$ 可能存在多条路线,我要找最短的。如果有多条最短的,我要字典序最小的。现在路线唯一了吧!比如这条路线最终是 +$i \rightarrow a \rightarrow b \rightarrow c \rightarrow d \rightarrow j$,则$path[i][j]=a$,也就是第一个后继节点。 +```cpp {.line-numbers} +#include +using namespace std; +const int INF = 0x3f3f3f3f; + +const int N = 1003; +int g[N][N]; // 邻接矩阵 +int n; // n个点 +int w[N]; // 额外费用 +int path[N][N]; // i->j 可能存在多条路线,我要找最短的。如果有多条最短的,我要字典序最小的。现在路线唯一了吧!比如这条路线最终是 +// i->a->b->c->d->j,则path[i][j]=a,也就是第一个后继节点。 +void floyd() { + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + if (g[i][k] != INF) // floyd优化 + for (int j = 1; j <= n; j++) { + if (g[i][j] > g[i][k] + g[k][j] + w[k]) { // w[k]:点权 + g[i][j] = g[i][k] + g[k][j] + w[k]; // k的加入,使得i->j的路径变短 + path[i][j] = path[i][k]; // 如果i->k->j使得i->j更近,那么根据定义path[i][j]就是这条最短路径中距离i最近的那个点,而这个点由于是出现在i->k的必经之路上,而且是i->k的首席弟子,所以,也必然是i->j的首席弟子。 + } + // 处理字典序 + if (g[i][j] == g[i][k] + g[k][j] + w[k]) { // 如果存在多条最短路径,也就是,除了k还有其它k1,k2使得i->j距离一样小 + if (path[i][j] > path[i][k]) path[i][j] = path[i][k]; // 字典序,谁更小就留下谁 + } + } +} +int main() { + while (cin >> n && n) { + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + path[i][j] = j; // 路径初始化,记录整条路径上,离i节点最近的,最短路径上的下一个点,只有i->j时,下一个点可不就是j + cin >> g[i][j]; // 不管是不是有边,都先录进来 + if (g[i][j] == -1) g[i][j] = INF; // 如果题目中给出的是无边,那么设置为正无穷。此时,有些记录的path[i][j]就是没用的,但没事,后面会被其它代码替换掉path[i][j]。 + } + } + for (int i = 1; i <= n; i++) cin >> w[i]; // 读入点权 + + // 多源最短路径 + floyd(); + + // 处理询问 + int x, y; + while (cin >> x >> y) { + if (x == -1 && y == -1) break; + printf("From %d to %d :\n", x, y); + printf("Path: %d", x); + int u = x, v = y; + // 理解路径思路: + // (1) 从起点x出发,用循环打印路径,最后一个打印的肯定是y + // (2) 从起点x出发,第二个点应该是离x最近的,并且是最短路径上的那个点,这个点就是path[x][y]! + // path[x][y]:从起点x出发,到终点y有多条最短路径,我们选择字典序最小的那条最短路径,然后path[x][y]就是从x出发,离x最近的这条最短路径上的点。 + while (x != y) { + printf("-->%d", path[x][y]); // 输出距离x最近的那个点 + x = path[x][y]; // 更换x概念,向y逼近,让循环跑起来 + } + + puts(""); + if (g[u][v] < INF) + printf("Total cost : %d\n", g[u][v]); + else + puts("-1"); + puts(""); + } + } + return 0; +} +``` https://juejin.cn/post/6935691567696969764 \ No newline at end of file From c9c962258f9e86d8e3f8934924d5eb1e0f76ba48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 4 Jan 2024 16:30:40 +0800 Subject: [PATCH 65/87] 'commit' --- .../T3/Floyd/{344.cpp => 344_1.cpp} | 0 TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp | 73 +++++++++++++++++++ TangDou/Topic/【Floyd专题】.md | 3 + 3 files changed, 76 insertions(+) rename TangDou/AcWing_TiGao/T3/Floyd/{344.cpp => 344_1.cpp} (100%) create mode 100644 TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344_1.cpp similarity index 100% rename from TangDou/AcWing_TiGao/T3/Floyd/344.cpp rename to TangDou/AcWing_TiGao/T3/Floyd/344_1.cpp diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp new file mode 100644 index 0000000..73d4710 --- /dev/null +++ b/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp @@ -0,0 +1,73 @@ +#include +using namespace std; +const int N = 110; +#define INF 0x7ffffff +/** +分析:模板题,理解floyd 的在 i , j 路径中没有包含k(因为此时k未用来更新),即可写出最小环 +在INF这里wa了两发,习惯值 1e9 不是最大值(查找时判断一下可以避免,但。。。懒了) +*/ +int n, m; +int mp[N][N]; +int g[N][N]; +int path[N][N]; +int ans[N]; +int cnt; +int mm; +void floyd() { + mm = INF; + for (int k = 1; k <= n; k++) { + for (int i = 1; i < k; i++) { + for (int j = i + 1; j < k; j++) { + int x = g[i][j] + mp[k][i] + mp[k][j]; + if (x < mm) { + mm = x; + int tmp = j; + cnt = 0; + + while (tmp != i) { + ans[cnt++] = tmp; + tmp = path[i][tmp]; + } + ans[cnt++] = i; + ans[cnt++] = k; + } + } + } + + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + if (g[i][j] > g[i][k] + g[k][j]) { + g[i][j] = g[i][k] + g[k][j]; + path[i][j] = path[k][j]; + } + } + } + } +} + +int main() { + while (cin >> n >> m) { + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + g[i][j] = mp[i][j] = INF; + path[i][j] = i; + } + } + + while (m--) { + int a, b, c; + cin >> a >> b >> c; + g[a][b] = g[b][a] = min(g[a][b], c); // 防重边 + mp[a][b] = mp[b][a] = g[a][b]; + } + + floyd(); + + if (mm == INF) { + puts("No solution."); + continue; + } + for (int i = 0; i < cnt; ++i) printf("%d%s", ans[i], (i == cnt - 1) ? "\n" : " "); + } + return 0; +} \ No newline at end of file diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 3b0b46b..9383145 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -180,6 +180,9 @@ int main() { return 0; } ``` +**$TODO$** +据说可以使用$Dijkstra$算法解决,有空可以试试: **[链接](https://blog.csdn.net/K_R_forever/article/details/80525757)** + https://juejin.cn/post/6935691567696969764 \ No newline at end of file From f9658350152a0e2ce9bef4db598bb01708bdef85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 4 Jan 2024 16:38:17 +0800 Subject: [PATCH 66/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp index 73d4710..2691678 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp @@ -7,8 +7,8 @@ const int N = 110; 在INF这里wa了两发,习惯值 1e9 不是最大值(查找时判断一下可以避免,但。。。懒了) */ int n, m; -int mp[N][N]; int g[N][N]; +int dis[N][N]; int path[N][N]; int ans[N]; int cnt; @@ -16,49 +16,49 @@ int mm; void floyd() { mm = INF; for (int k = 1; k <= n; k++) { + // dp for (int i = 1; i < k; i++) { for (int j = i + 1; j < k; j++) { - int x = g[i][j] + mp[k][i] + mp[k][j]; + int x = dis[i][j] + g[k][i] + g[k][j]; if (x < mm) { mm = x; - int tmp = j; + int tg = j; cnt = 0; - while (tmp != i) { - ans[cnt++] = tmp; - tmp = path[i][tmp]; + while (tg != i) { + ans[cnt++] = tg; + tg = path[i][tg]; } ans[cnt++] = i; ans[cnt++] = k; } } } - - for (int i = 1; i <= n; i++) { - for (int j = 1; j <= n; j++) { - if (g[i][j] > g[i][k] + g[k][j]) { - g[i][j] = g[i][k] + g[k][j]; - path[i][j] = path[k][j]; + // floyd + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + if (dis[i][j] > dis[i][k] + dis[k][j]) { + dis[i][j] = dis[i][k] + dis[k][j]; + path[i][j] = path[k][j]; // 这咋还和我理解的不一样呢? } - } - } } } int main() { while (cin >> n >> m) { - for (int i = 1; i <= n; i++) { + // 邻接矩阵初始化 + for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { - g[i][j] = mp[i][j] = INF; + dis[i][j] = g[i][j] = INF; path[i][j] = i; } - } + // 读入边 while (m--) { int a, b, c; cin >> a >> b >> c; - g[a][b] = g[b][a] = min(g[a][b], c); // 防重边 - mp[a][b] = mp[b][a] = g[a][b]; + g[a][b] = g[b][a] = min(g[a][b], c); + dis[a][b] = dis[b][a] = g[a][b]; } floyd(); @@ -67,7 +67,7 @@ int main() { puts("No solution."); continue; } - for (int i = 0; i < cnt; ++i) printf("%d%s", ans[i], (i == cnt - 1) ? "\n" : " "); + for (int i = 0; i < cnt; i++) printf("%d%s", ans[i], (i == cnt - 1) ? "\n" : " "); } return 0; } \ No newline at end of file From 1cf4c0e7baa6737f3d5729e86a892ef9d068deb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 4 Jan 2024 16:43:07 +0800 Subject: [PATCH 67/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp | 30 ++++++++++++------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp index 2691678..1610d2c 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp @@ -8,29 +8,27 @@ const int N = 110; */ int n, m; int g[N][N]; -int dis[N][N]; +int dis[N][N]; // dp结果数组 int path[N][N]; int ans[N]; int cnt; -int mm; +int res = INF; void floyd() { - mm = INF; for (int k = 1; k <= n; k++) { // dp for (int i = 1; i < k; i++) { - for (int j = i + 1; j < k; j++) { - int x = dis[i][j] + g[k][i] + g[k][j]; - if (x < mm) { - mm = x; - int tg = j; - cnt = 0; + for (int j = i + 1; j < k; j++) { // i,j,k序号由小到大 + if (res > dis[i][j] + g[i][k] + g[k][j]) { + res = dis[i][j] + g[i][k] + g[k][j]; - while (tg != i) { - ans[cnt++] = tg; - tg = path[i][tg]; + int x = j, y = i; + cnt = 0; + while (x != y) { + ans[cnt++] = x; + x = path[i][x]; } - ans[cnt++] = i; - ans[cnt++] = k; + ans[cnt++] = y; + ans[cnt++] = k; // 序号最大的节点k } } } @@ -50,7 +48,7 @@ int main() { for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { dis[i][j] = g[i][j] = INF; - path[i][j] = i; + path[i][j] = i; // 这里也是不一样,需要思考与整理 } // 读入边 @@ -63,7 +61,7 @@ int main() { floyd(); - if (mm == INF) { + if (res == INF) { puts("No solution."); continue; } From f4ea07cc6d3182076ae6075e68c3d6596f59e579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Thu, 4 Jan 2024 16:57:07 +0800 Subject: [PATCH 68/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp index 1610d2c..9e501a0 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp @@ -1,10 +1,9 @@ #include using namespace std; const int N = 110; -#define INF 0x7ffffff +#define INF 0x3f3f3f3f /** 分析:模板题,理解floyd 的在 i , j 路径中没有包含k(因为此时k未用来更新),即可写出最小环 -在INF这里wa了两发,习惯值 1e9 不是最大值(查找时判断一下可以避免,但。。。懒了) */ int n, m; int g[N][N]; @@ -17,17 +16,17 @@ void floyd() { for (int k = 1; k <= n; k++) { // dp for (int i = 1; i < k; i++) { - for (int j = i + 1; j < k; j++) { // i,j,k序号由小到大 - if (res > dis[i][j] + g[i][k] + g[k][j]) { + for (int j = i + 1; j < k; j++) { // i,j,k序号由小到大 + if (res - dis[i][j] > g[i][k] + g[k][j]) { // 减法防溢出 res = dis[i][j] + g[i][k] + g[k][j]; - int x = j, y = i; - cnt = 0; + int x = i, y = j; + cnt = 0; // 以前有过的路径也清空 while (x != y) { - ans[cnt++] = x; - x = path[i][x]; + ans[cnt++] = y; + y = path[i][y]; } - ans[cnt++] = y; + ans[cnt++] = x; ans[cnt++] = k; // 序号最大的节点k } } From fe1e1b9cb13e428ccace1ec6211acf344247c19e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 08:03:35 +0800 Subject: [PATCH 69/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp | 70 +++++++++++++++ TangDou/Topic/【Floyd专题】.md | 114 +++++++++++++++++++++++- 2 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp new file mode 100644 index 0000000..9e501a0 --- /dev/null +++ b/TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp @@ -0,0 +1,70 @@ +#include +using namespace std; +const int N = 110; +#define INF 0x3f3f3f3f +/** +分析:模板题,理解floyd 的在 i , j 路径中没有包含k(因为此时k未用来更新),即可写出最小环 +*/ +int n, m; +int g[N][N]; +int dis[N][N]; // dp结果数组 +int path[N][N]; +int ans[N]; +int cnt; +int res = INF; +void floyd() { + for (int k = 1; k <= n; k++) { + // dp + for (int i = 1; i < k; i++) { + for (int j = i + 1; j < k; j++) { // i,j,k序号由小到大 + if (res - dis[i][j] > g[i][k] + g[k][j]) { // 减法防溢出 + res = dis[i][j] + g[i][k] + g[k][j]; + + int x = i, y = j; + cnt = 0; // 以前有过的路径也清空 + while (x != y) { + ans[cnt++] = y; + y = path[i][y]; + } + ans[cnt++] = x; + ans[cnt++] = k; // 序号最大的节点k + } + } + } + // floyd + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + if (dis[i][j] > dis[i][k] + dis[k][j]) { + dis[i][j] = dis[i][k] + dis[k][j]; + path[i][j] = path[k][j]; // 这咋还和我理解的不一样呢? + } + } +} + +int main() { + while (cin >> n >> m) { + // 邻接矩阵初始化 + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) { + dis[i][j] = g[i][j] = INF; + path[i][j] = i; // 这里也是不一样,需要思考与整理 + } + + // 读入边 + while (m--) { + int a, b, c; + cin >> a >> b >> c; + g[a][b] = g[b][a] = min(g[a][b], c); + dis[a][b] = dis[b][a] = g[a][b]; + } + + floyd(); + + if (res == INF) { + puts("No solution."); + continue; + } + for (int i = 0; i < cnt; i++) printf("%d%s", ans[i], (i == cnt - 1) ? "\n" : " "); + } + return 0; +} \ No newline at end of file diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 9383145..a764ef4 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -25,7 +25,7 @@ void floyd() { } ``` -### 三、例题 +### 三、判负环 #### [$POJ-3259$ $Wormholes$](https://link.juejin.cn/?target=https%3A%2F%2Fvjudge.net%2Fproblem%2FPOJ-3259) @@ -97,6 +97,118 @@ int main() { } ``` +### 四、记录最短路径并输出 + +$floyd$ 算法求最短路(边权可为负)很优美,四行代码就搞定了。今天做了一个题,可以用 $floyd$ 做,但是要最短路的路径。在网上搜了一阵,代码倒是有,但是没有解释,为何是这样的?于是,手推了一遍,写了这篇博客。 + +不像 $dijkstra$ 和 $spfa$,是一个点一个点加进去的,直接 $pre$ 数组往前倒,倒至起点就行了。$floyd$ 是基于动态规划,这怎么记录路径呢? + +开一个 $path$数组,$path[i][j]$ 表示:更新从 $i$ 到 $j$ 的最短路径时,经过的一个中转点。 + +```cpp {.line-numbers} +void floyd(){ + for(int k=1;k<=n;k++) + for(int i=1;i<=n;i++) + for(int j=1;j<=n;j++) + if(dis[i][j]>dis[i][k]+dis[k][j]){ + dis[i][j]=dis[i][k]+dis[k][j]; + path[i][j]=k; + } +} +``` +![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401050754691.png) + +在这个图中,很容易看出,从 $1$ 到 $6$ 之间的最短路径是标红的那几条边。 + +二重循环所有点,我们输出 $path$ 数组: +```cpp {.line-numbers} +0 0 0 3 4 5 +0 0 0 0 4 5 +0 0 0 0 4 5 +0 0 0 0 0 5 +0 0 0 0 0 0 +0 0 0 0 0 0 +``` +可以看出,$path[1][6]$ 是 $5$,$path[1][5]$ 是 $4$,$path[1][4]$ 是 $3$。那么,最终 $path[i][j]$ 中存的就是 **从$i$到$j$的最短路径中的最后一个点**。 + +而我们最终输出路径的思路就是,不断分段最短路径! 最后输出所有的点。 +> **原理:由 $i$ 到 $j$ 的最短路径中的一点 $k$,将最短路径分段为从 $i$ 到 $k$ 的最短路径 和 从 $k$ 到 $j$ 的最短路径,最短路径就为从$i$到$k$的最短路径+从$k$到$j$的最短路径,一直分段,直到分到 $i$ 和 $j$ 为同一点,停止** + +可能现在你有些迷糊,我们直接看代码吧! + +```cpp {.line-numbers} +void get_path(int i,int j){//path[i][j]:从i到j最短路径中经过的一点k + if(i==j) return; //分段到同一点,停止 + if(path[i][j]==0) cout<dist[i][k]+dist[k][j]){ + dist[i][j]=dist[i][k]+dist[k][j]; + pass[i][j]=k; + } +} +void print(int i,int j){ + if(i==j) return; + if(pass[i][j]==0) cout< Date: Fri, 5 Jan 2024 08:46:27 +0800 Subject: [PATCH 70/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp | 2 +- TangDou/Topic/HDU1385.cpp | 80 ++++++------- TangDou/Topic/【Floyd专题】.md | 111 +++++++++--------- .../【最短路径】Dijkstra算法专题.md | 6 + 4 files changed, 97 insertions(+), 102 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp index 9e501a0..cf3bcab 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp +++ b/TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp @@ -67,4 +67,4 @@ int main() { for (int i = 0; i < cnt; i++) printf("%d%s", ans[i], (i == cnt - 1) ? "\n" : " "); } return 0; -} \ No newline at end of file +} diff --git a/TangDou/Topic/HDU1385.cpp b/TangDou/Topic/HDU1385.cpp index b0440f2..c04c083 100644 --- a/TangDou/Topic/HDU1385.cpp +++ b/TangDou/Topic/HDU1385.cpp @@ -1,64 +1,54 @@ #include using namespace std; + +const int N = 110; const int INF = 0x3f3f3f3f; +// Floyd+记录起点后继 +int n; +int g[N][N], w[N]; +int path[N][N]; // 记录i到j最短路径中i的后继 -const int N = 1003; -int g[N][N]; // 邻接矩阵 -int n; // n个点 -int w[N]; // 额外费用 -int path[N][N]; // i->j 可能存在多条路线,我要找最短的。如果有多条最短的,我要字典序最小的。现在路线唯一了吧!比如这条路线最终是 -// i->a->b->c->d->j,则path[i][j]=a,也就是第一个后继节点。 void floyd() { for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) - if (g[i][k] != INF) // floyd优化 - for (int j = 1; j <= n; j++) { - if (g[i][j] > g[i][k] + g[k][j] + w[k]) { // w[k]:点权 - g[i][j] = g[i][k] + g[k][j] + w[k]; // k的加入,使得i->j的路径变短 - path[i][j] = path[i][k]; // 如果i->k->j使得i->j更近,那么根据定义path[i][j]就是这条最短路径中距离i最近的那个点,而这个点由于是出现在i->k的必经之路上,而且是i->k的首席弟子,所以,也必然是i->j的首席弟子。 - } - // 处理字典序 - if (g[i][j] == g[i][k] + g[k][j] + w[k]) { // 如果存在多条最短路径,也就是,除了k还有其它k1,k2使得i->j距离一样小 - if (path[i][j] > path[i][k]) path[i][j] = path[i][k]; // 字典序,谁更小就留下谁 - } + for (int j = 1; j <= n; j++) { + if (g[i][j] > g[i][k] + g[k][j] + w[k]) { + g[i][j] = g[i][k] + g[k][j] + w[k]; + path[i][j] = path[i][k]; // i->j这条最短路径上,i后面第一个节点,是i->k路径上第一个节点 } + // 相同路径下选择后继更小的(为了字典序) + if (g[i][j] == g[i][k] + g[k][j] + w[k]) + if (path[i][j] > path[i][k]) + path[i][j] = path[i][k]; + } +} + +// 递归输出路径 +void print(int s, int e) { + printf("-->%d", path[s][e]); // 输出s的后继 + if (path[s][e] != e) // 如果不是直连 + print(path[s][e], e); // 递归输出后继 } + int main() { - while (cin >> n && n) { - for (int i = 1; i <= n; i++) { + while (cin >> n, n) { + for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { - path[i][j] = j; // 路径初始化,记录整条路径上,离i节点最近的,最短路径上的下一个点,只有i->j时,下一个点可不就是j - cin >> g[i][j]; // 不管是不是有边,都先录进来 - if (g[i][j] == -1) g[i][j] = INF; // 如果题目中给出的是无边,那么设置为正无穷。此时,有些记录的path[i][j]就是没用的,但没事,后面会被其它代码替换掉path[i][j]。 + cin >> g[i][j]; + if (g[i][j] == -1) g[i][j] = INF; + path[i][j] = j; } - } - for (int i = 1; i <= n; i++) cin >> w[i]; // 读入点权 - // 多源最短路径 + for (int i = 1; i <= n; i++) cin >> w[i]; floyd(); - // 处理询问 - int x, y; - while (cin >> x >> y) { - if (x == -1 && y == -1) break; - printf("From %d to %d :\n", x, y); - printf("Path: %d", x); - int u = x, v = y; - // 理解路径思路: - // (1) 从起点x出发,用循环打印路径,最后一个打印的肯定是y - // (2) 从起点x出发,第二个点应该是离x最近的,并且是最短路径上的那个点,这个点就是path[x][y]! - // path[x][y]:从起点x出发,到终点y有多条最短路径,我们选择字典序最小的那条最短路径,然后path[x][y]就是从x出发,离x最近的这条最短路径上的点。 - while (x != y) { - printf("-->%d", path[x][y]); // 输出距离x最近的那个点 - x = path[x][y]; // 更换x概念,向y逼近,让循环跑起来 - } + int s, e; - puts(""); - if (g[u][v] < INF) - printf("Total cost : %d\n", g[u][v]); - else - puts("-1"); - puts(""); + while (cin >> s >> e, ~s && ~e) { + printf("From %d to %d :\n", s, e); + printf("Path: %d", s); + if (s != e) print(s, e); // 起点与终点不同开始递归 + printf("\nTotal cost : %d\n\n", g[s][e]); } } return 0; diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index a764ef4..c9a9199 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -106,21 +106,21 @@ $floyd$ 算法求最短路(边权可为负)很优美,四行代码就搞定 开一个 $path$数组,$path[i][j]$ 表示:更新从 $i$ 到 $j$ 的最短路径时,经过的一个中转点。 ```cpp {.line-numbers} -void floyd(){ - for(int k=1;k<=n;k++) - for(int i=1;i<=n;i++) - for(int j=1;j<=n;j++) - if(dis[i][j]>dis[i][k]+dis[k][j]){ - dis[i][j]=dis[i][k]+dis[k][j]; - path[i][j]=k; - } +void floyd() { + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + if (dis[i][j] > dis[i][k] + dis[k][j]) { + dis[i][j] = dis[i][k] + dis[k][j]; + path[i][j] = k; //记录i->j是通过k转移的 + } } ``` ![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401050754691.png) 在这个图中,很容易看出,从 $1$ 到 $6$ 之间的最短路径是标红的那几条边。 -二重循环所有点,我们输出 $path$ 数组: +二重循环所有点,输出 $path$ 数组: ```cpp {.line-numbers} 0 0 0 3 4 5 0 0 0 0 4 5 @@ -129,7 +129,7 @@ void floyd(){ 0 0 0 0 0 0 0 0 0 0 0 0 ``` -可以看出,$path[1][6]$ 是 $5$,$path[1][5]$ 是 $4$,$path[1][4]$ 是 $3$。那么,最终 $path[i][j]$ 中存的就是 **从$i$到$j$的最短路径中的最后一个点**。 +可以看出,$path[1][6]$ 是 $5$,$path[1][5]$ 是 $4$,$path[1][4]$ 是 $3$。那么,最终 $path[i][j]$ 中存的就是 **从$i$到$j$的最短路径中的靠近$j$的最后一个点**。 而我们最终输出路径的思路就是,不断分段最短路径! 最后输出所有的点。 > **原理:由 $i$ 到 $j$ 的最短路径中的一点 $k$,将最短路径分段为从 $i$ 到 $k$ 的最短路径 和 从 $k$ 到 $j$ 的最短路径,最短路径就为从$i$到$k$的最短路径+从$k$到$j$的最短路径,一直分段,直到分到 $i$ 和 $j$ 为同一点,停止** @@ -137,31 +137,32 @@ void floyd(){ 可能现在你有些迷糊,我们直接看代码吧! ```cpp {.line-numbers} -void get_path(int i,int j){//path[i][j]:从i到j最短路径中经过的一点k - if(i==j) return; //分段到同一点,停止 - if(path[i][j]==0) cout<dist[i][k]+dist[k][j]){ - dist[i][j]=dist[i][k]+dist[k][j]; - pass[i][j]=k; - } +void floyd() { + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + if (dis[i][j] > dis[i][k] + dis[k][j]) { + dis[i][j] = dis[i][k] + dis[k][j]; + path[i][j] = k; + } } -void print(int i,int j){ - if(i==j) return; - if(pass[i][j]==0) cout< Date: Fri, 5 Jan 2024 08:59:01 +0800 Subject: [PATCH 71/87] 'commit' --- TangDou/Topic/HDU1385.in | 12 +++ TangDou/Topic/HDU1385_Precursor.cpp | 71 ++++++++++++++++ .../{HDU1385.cpp => HDU1385_Subsequent.cpp} | 15 ++++ TangDou/Topic/【Floyd专题】.md | 80 ++++++++----------- 4 files changed, 133 insertions(+), 45 deletions(-) create mode 100644 TangDou/Topic/HDU1385.in create mode 100644 TangDou/Topic/HDU1385_Precursor.cpp rename TangDou/Topic/{HDU1385.cpp => HDU1385_Subsequent.cpp} (85%) diff --git a/TangDou/Topic/HDU1385.in b/TangDou/Topic/HDU1385.in new file mode 100644 index 0000000..722ece9 --- /dev/null +++ b/TangDou/Topic/HDU1385.in @@ -0,0 +1,12 @@ +5 +0 3 22 -1 4 +3 0 5 -1 -1 +22 5 0 9 20 +-1 -1 9 0 4 +4 -1 20 4 0 +5 17 8 3 1 +1 3 +3 5 +2 4 +-1 -1 +0 \ No newline at end of file diff --git a/TangDou/Topic/HDU1385_Precursor.cpp b/TangDou/Topic/HDU1385_Precursor.cpp new file mode 100644 index 0000000..3c4d321 --- /dev/null +++ b/TangDou/Topic/HDU1385_Precursor.cpp @@ -0,0 +1,71 @@ +#include +using namespace std; + +const int N = 110; +const int INF = 0x3f3f3f3f; +// Floyd+记录终点前驱 +int n; +int g[N][N], w[N]; +int path[N][N]; // 记录i到j最短路径中j的前驱 + +void floyd() { + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) { + if (g[i][j] > g[i][k] + g[k][j] + w[k]) { + g[i][j] = g[i][k] + g[k][j] + w[k]; + path[i][j] = path[i][k]; // i->j这条最短路径上,i后面第一个节点,是i->k路径上第一个节点 + } + // 相同路径下选择后继更小的(为了字典序) + if (g[i][j] == g[i][k] + g[k][j] + w[k]) + if (path[i][j] > path[i][k]) + path[i][j] = path[i][k]; + } +} + +// 递归输出路径 +void print(int s, int e) { + printf("-->%d", path[s][e]); // 输出s的后继 + if (path[s][e] != e) // 如果不是直连 + print(path[s][e], e); // 递归输出后继 +} + +/* +From 1 to 3 : +Path: 1-->5-->4-->3 +Total cost : 21 + +From 3 to 5 : +Path: 3-->4-->5 +Total cost : 16 + +From 2 to 4 : +Path: 2-->1-->5-->4 +Total cost : 17 +*/ +int main() { +#ifndef ONLINE_JUDGE + freopen("HDU1385.in", "r", stdin); +#endif + while (cin >> n, n) { + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) { + cin >> g[i][j]; + if (g[i][j] == -1) g[i][j] = INF; + path[i][j] = j; + } + + for (int i = 1; i <= n; i++) cin >> w[i]; + floyd(); + + int s, e; + + while (cin >> s >> e, ~s && ~e) { + printf("From %d to %d :\n", s, e); + printf("Path: %d", s); + if (s != e) print(s, e); // 起点与终点不同开始递归 + printf("\nTotal cost : %d\n\n", g[s][e]); + } + } + return 0; +} \ No newline at end of file diff --git a/TangDou/Topic/HDU1385.cpp b/TangDou/Topic/HDU1385_Subsequent.cpp similarity index 85% rename from TangDou/Topic/HDU1385.cpp rename to TangDou/Topic/HDU1385_Subsequent.cpp index c04c083..9f519e4 100644 --- a/TangDou/Topic/HDU1385.cpp +++ b/TangDou/Topic/HDU1385_Subsequent.cpp @@ -29,8 +29,23 @@ void print(int s, int e) { if (path[s][e] != e) // 如果不是直连 print(path[s][e], e); // 递归输出后继 } +/* +From 1 to 3 : +Path: 1-->5-->4-->3 +Total cost : 21 +From 3 to 5 : +Path: 3-->4-->5 +Total cost : 16 + +From 2 to 4 : +Path: 2-->1-->5-->4 +Total cost : 17 +*/ int main() { +#ifndef ONLINE_JUDGE + freopen("HDU1385.in", "r", stdin); +#endif while (cin >> n, n) { for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index c9a9199..10e5f57 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -227,65 +227,55 @@ $i \rightarrow a \rightarrow b \rightarrow c \rightarrow d \rightarrow j$,则$pa ```cpp {.line-numbers} #include using namespace std; + +const int N = 110; const int INF = 0x3f3f3f3f; +// Floyd+记录起点后继 +int n; +int g[N][N], w[N]; +int path[N][N]; // 记录i到j最短路径中i的后继 -const int N = 1003; -int g[N][N]; // 邻接矩阵 -int n; // n个点 -int w[N]; // 额外费用 -int path[N][N]; // i->j 可能存在多条路线,我要找最短的。如果有多条最短的,我要字典序最小的。现在路线唯一了吧!比如这条路线最终是 -// i->a->b->c->d->j,则path[i][j]=a,也就是第一个后继节点。 void floyd() { for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) - if (g[i][k] != INF) // floyd优化 - for (int j = 1; j <= n; j++) { - if (g[i][j] > g[i][k] + g[k][j] + w[k]) { // w[k]:点权 - g[i][j] = g[i][k] + g[k][j] + w[k]; // k的加入,使得i->j的路径变短 - path[i][j] = path[i][k]; // 如果i->k->j使得i->j更近,那么根据定义path[i][j]就是这条最短路径中距离i最近的那个点,而这个点由于是出现在i->k的必经之路上,而且是i->k的首席弟子,所以,也必然是i->j的首席弟子。 - } - // 处理字典序 - if (g[i][j] == g[i][k] + g[k][j] + w[k]) { // 如果存在多条最短路径,也就是,除了k还有其它k1,k2使得i->j距离一样小 - if (path[i][j] > path[i][k]) path[i][j] = path[i][k]; // 字典序,谁更小就留下谁 - } + for (int j = 1; j <= n; j++) { + if (g[i][j] > g[i][k] + g[k][j] + w[k]) { + g[i][j] = g[i][k] + g[k][j] + w[k]; + path[i][j] = path[i][k]; // i->j这条最短路径上,i后面第一个节点,是i->k路径上第一个节点 } + // 相同路径下选择后继更小的(为了字典序) + if (g[i][j] == g[i][k] + g[k][j] + w[k]) + if (path[i][j] > path[i][k]) + path[i][j] = path[i][k]; + } +} + +// 递归输出路径 +void print(int s, int e) { + printf("-->%d", path[s][e]); // 输出s的后继 + if (path[s][e] != e) // 如果不是直连 + print(path[s][e], e); // 递归输出后继 } + int main() { - while (cin >> n && n) { - for (int i = 1; i <= n; i++) { + while (cin >> n, n) { + for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { - path[i][j] = j; // 路径初始化,记录整条路径上,离i节点最近的,最短路径上的下一个点,只有i->j时,下一个点可不就是j - cin >> g[i][j]; // 不管是不是有边,都先录进来 - if (g[i][j] == -1) g[i][j] = INF; // 如果题目中给出的是无边,那么设置为正无穷。此时,有些记录的path[i][j]就是没用的,但没事,后面会被其它代码替换掉path[i][j]。 + cin >> g[i][j]; + if (g[i][j] == -1) g[i][j] = INF; + path[i][j] = j; } - } - for (int i = 1; i <= n; i++) cin >> w[i]; // 读入点权 - // 多源最短路径 + for (int i = 1; i <= n; i++) cin >> w[i]; floyd(); - // 处理询问 - int x, y; - while (cin >> x >> y) { - if (x == -1 && y == -1) break; - printf("From %d to %d :\n", x, y); - printf("Path: %d", x); - int u = x, v = y; - // 理解路径思路: - // (1) 从起点x出发,用循环打印路径,最后一个打印的肯定是y - // (2) 从起点x出发,第二个点应该是离x最近的,并且是最短路径上的那个点,这个点就是path[x][y]! - // path[x][y]:从起点x出发,到终点y有多条最短路径,我们选择字典序最小的那条最短路径,然后path[x][y]就是从x出发,离x最近的这条最短路径上的点。 - while (x != y) { - printf("-->%d", path[x][y]); // 输出距离x最近的那个点 - x = path[x][y]; // 更换x概念,向y逼近,让循环跑起来 - } + int s, e; - puts(""); - if (g[u][v] < INF) - printf("Total cost : %d\n", g[u][v]); - else - puts("-1"); - puts(""); + while (cin >> s >> e, ~s && ~e) { + printf("From %d to %d :\n", s, e); + printf("Path: %d", s); + if (s != e) print(s, e); // 起点与终点不同开始递归 + printf("\nTotal cost : %d\n\n", g[s][e]); } } return 0; From a3064e57406df35ef44603ca5f42a365e63efee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 09:03:54 +0800 Subject: [PATCH 72/87] 'commit' --- .../{HDU1385_Subsequent.cpp => HDU1385.cpp} | 0 TangDou/Topic/HDU1385_Precursor.cpp | 71 ------------------- 2 files changed, 71 deletions(-) rename TangDou/Topic/{HDU1385_Subsequent.cpp => HDU1385.cpp} (100%) delete mode 100644 TangDou/Topic/HDU1385_Precursor.cpp diff --git a/TangDou/Topic/HDU1385_Subsequent.cpp b/TangDou/Topic/HDU1385.cpp similarity index 100% rename from TangDou/Topic/HDU1385_Subsequent.cpp rename to TangDou/Topic/HDU1385.cpp diff --git a/TangDou/Topic/HDU1385_Precursor.cpp b/TangDou/Topic/HDU1385_Precursor.cpp deleted file mode 100644 index 3c4d321..0000000 --- a/TangDou/Topic/HDU1385_Precursor.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include -using namespace std; - -const int N = 110; -const int INF = 0x3f3f3f3f; -// Floyd+记录终点前驱 -int n; -int g[N][N], w[N]; -int path[N][N]; // 记录i到j最短路径中j的前驱 - -void floyd() { - for (int k = 1; k <= n; k++) - for (int i = 1; i <= n; i++) - for (int j = 1; j <= n; j++) { - if (g[i][j] > g[i][k] + g[k][j] + w[k]) { - g[i][j] = g[i][k] + g[k][j] + w[k]; - path[i][j] = path[i][k]; // i->j这条最短路径上,i后面第一个节点,是i->k路径上第一个节点 - } - // 相同路径下选择后继更小的(为了字典序) - if (g[i][j] == g[i][k] + g[k][j] + w[k]) - if (path[i][j] > path[i][k]) - path[i][j] = path[i][k]; - } -} - -// 递归输出路径 -void print(int s, int e) { - printf("-->%d", path[s][e]); // 输出s的后继 - if (path[s][e] != e) // 如果不是直连 - print(path[s][e], e); // 递归输出后继 -} - -/* -From 1 to 3 : -Path: 1-->5-->4-->3 -Total cost : 21 - -From 3 to 5 : -Path: 3-->4-->5 -Total cost : 16 - -From 2 to 4 : -Path: 2-->1-->5-->4 -Total cost : 17 -*/ -int main() { -#ifndef ONLINE_JUDGE - freopen("HDU1385.in", "r", stdin); -#endif - while (cin >> n, n) { - for (int i = 1; i <= n; i++) - for (int j = 1; j <= n; j++) { - cin >> g[i][j]; - if (g[i][j] == -1) g[i][j] = INF; - path[i][j] = j; - } - - for (int i = 1; i <= n; i++) cin >> w[i]; - floyd(); - - int s, e; - - while (cin >> s >> e, ~s && ~e) { - printf("From %d to %d :\n", s, e); - printf("Path: %d", s); - if (s != e) print(s, e); // 起点与终点不同开始递归 - printf("\nTotal cost : %d\n\n", g[s][e]); - } - } - return 0; -} \ No newline at end of file From 1c107e9ef87c1327c26721acdf3652997babe8d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 09:09:30 +0800 Subject: [PATCH 73/87] 'commit' --- TangDou/Topic/【Floyd专题】.md | 141 ++++++----------------------- 1 file changed, 28 insertions(+), 113 deletions(-) diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 10e5f57..63860a7 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -97,116 +97,7 @@ int main() { } ``` -### 四、记录最短路径并输出 - -$floyd$ 算法求最短路(边权可为负)很优美,四行代码就搞定了。今天做了一个题,可以用 $floyd$ 做,但是要最短路的路径。在网上搜了一阵,代码倒是有,但是没有解释,为何是这样的?于是,手推了一遍,写了这篇博客。 - -不像 $dijkstra$ 和 $spfa$,是一个点一个点加进去的,直接 $pre$ 数组往前倒,倒至起点就行了。$floyd$ 是基于动态规划,这怎么记录路径呢? - -开一个 $path$数组,$path[i][j]$ 表示:更新从 $i$ 到 $j$ 的最短路径时,经过的一个中转点。 - -```cpp {.line-numbers} -void floyd() { - for (int k = 1; k <= n; k++) - for (int i = 1; i <= n; i++) - for (int j = 1; j <= n; j++) - if (dis[i][j] > dis[i][k] + dis[k][j]) { - dis[i][j] = dis[i][k] + dis[k][j]; - path[i][j] = k; //记录i->j是通过k转移的 - } -} -``` -![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401050754691.png) - -在这个图中,很容易看出,从 $1$ 到 $6$ 之间的最短路径是标红的那几条边。 - -二重循环所有点,输出 $path$ 数组: -```cpp {.line-numbers} -0 0 0 3 4 5 -0 0 0 0 4 5 -0 0 0 0 4 5 -0 0 0 0 0 5 -0 0 0 0 0 0 -0 0 0 0 0 0 -``` -可以看出,$path[1][6]$ 是 $5$,$path[1][5]$ 是 $4$,$path[1][4]$ 是 $3$。那么,最终 $path[i][j]$ 中存的就是 **从$i$到$j$的最短路径中的靠近$j$的最后一个点**。 - -而我们最终输出路径的思路就是,不断分段最短路径! 最后输出所有的点。 -> **原理:由 $i$ 到 $j$ 的最短路径中的一点 $k$,将最短路径分段为从 $i$ 到 $k$ 的最短路径 和 从 $k$ 到 $j$ 的最短路径,最短路径就为从$i$到$k$的最短路径+从$k$到$j$的最短路径,一直分段,直到分到 $i$ 和 $j$ 为同一点,停止** - -可能现在你有些迷糊,我们直接看代码吧! - -```cpp {.line-numbers} -void print(int i, int j) { // path[i][j]:从i到j最短路径中经过的一点k - if (i == j) return; // 分段到同一点,递归结束 - if (path[i][j] == 0) // i和j直接相连,就是i到j最短路径不经过任何点 - cout << i << " " << j << endl; - else { - print(i, path[i][j]); // 分段输出从i到k的最短路径 - // 输出从i到k最短路径中的所有点(一定都在从i到j的最短路径中) - print(path[i][j], j); // 分段输出从k到j的最短路径 - // 输出从j到k最短路径中的所有的点 - } -} -``` -就是一个 **递归** 的过程。 - -我们用上面的图模拟一下: -首先,从 $1$ 到 $6$ 的最短路径,$path[1][6]$中存的是:该最短路径中的最后一个节点 $5$,即,$k = 5$。 - -那么,递归(分段) 到,从 $1$到$5$的最短路 和 从$5$到$6$的最短路。 -- 从$1$到$5$的最短路,$path[1][5]=4$,则又分段为从$1$到$4$的最短路和从$4$到$5$的最短路。 -- 从$1$到$4$的最短路,$path[1][4]=3$,则又分段为从$1$到$3$的最短路和从$3$到$4$的最短路。 -- $path[1][3]=0$!如图,$1$和$3$直接相连!那么$1$和$3$都是最短路中的点,输出就行了! - -**回溯**: -- 从$3$到$4$的最短路,$path[3][4]=0$!$3$和$4$直接相连,$3$和$4$都是最短路中的点,输出! -- 从$4$到$5$的最短路,$path[4][5]=0$!$4$和$5$直接相连,$4$和$5$都是最短路中的点,输出! -- 从$5$到$6$的最短路,$path[5][6]=0$!$5$和$6$直接相连,$5$和$6$都是最短路中的点,输出! - -所以,最终输出的就是: -```cpp {.line-numbers} -1 3 -3 4 -4 5 -5 6 -``` -依次连接就是从$1$到$6$的最短路径了! - -**总体代码** -```cpp {.line-numbers} -void floyd() { - for (int k = 1; k <= n; k++) - for (int i = 1; i <= n; i++) - for (int j = 1; j <= n; j++) - if (dis[i][j] > dis[i][k] + dis[k][j]) { - dis[i][j] = dis[i][k] + dis[k][j]; - path[i][j] = k; - } -} -void print(int i, int j) { - if (i == j) return; - if (path[i][j] == 0) - cout << i << " " << j << endl; - else { - print(i, path[i][j]); - print(path[i][j], j); - } -} - -int main() { - ··· //一顿初始化,输入数据 - - floyd(); - print(1, n); // 输出从1到n的最短路径中的所有点(边) - - return 0; -} - -``` -**练习题**: - - +### 四、练习题 #### [$HDU-1385$ $Minimum$ $Transport$ $Cost$](http://acm.hdu.edu.cn/showproblem.php?pid=1385) @@ -256,8 +147,23 @@ void print(int s, int e) { if (path[s][e] != e) // 如果不是直连 print(path[s][e], e); // 递归输出后继 } - +/* +From 1 to 3 : +Path: 1-->5-->4-->3 +Total cost : 21 + +From 3 to 5 : +Path: 3-->4-->5 +Total cost : 16 + +From 2 to 4 : +Path: 2-->1-->5-->4 +Total cost : 17 +*/ int main() { +#ifndef ONLINE_JUDGE + freopen("HDU1385.in", "r", stdin); +#endif while (cin >> n, n) { for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { @@ -281,9 +187,18 @@ int main() { return 0; } ``` -**$TODO$** -据说可以使用$Dijkstra$算法解决,有空可以试试: **[链接](https://blog.csdn.net/K_R_forever/article/details/80525757)** +#### [$HDU$-$1599$ $find$ $the$ $mincost$ $route$](https://acm.hdu.edu.cn/showproblem.php?pid=1599) +(最小环) + +#### [$HDU$-$1704$ $Rank$](https://acm.hdu.edu.cn/showproblem.php?pid=1704) +(传递闭包) + +#### [$HDU$-$3631$ $Shortest$ $Path$](https://acm.hdu.edu.cn/showproblem.php?pid=3631) +(变形) + +**$TODO$** +据说可以使用$Dijkstra$算法解决,有空可以试试: **[链接](https://blog.csdn.net/K_R_forever/article/details/80525757)** https://juejin.cn/post/6935691567696969764 \ No newline at end of file From 4643a1b272e898eb58be5d10c99d1768fabdd9e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 09:18:58 +0800 Subject: [PATCH 74/87] 'commit' --- TangDou/Topic/HDU1599.cpp | 46 ++++++++++++++++++++++++++++++ TangDou/Topic/【Floyd专题】.md | 30 ++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 TangDou/Topic/HDU1599.cpp diff --git a/TangDou/Topic/HDU1599.cpp b/TangDou/Topic/HDU1599.cpp new file mode 100644 index 0000000..d5f2ddd --- /dev/null +++ b/TangDou/Topic/HDU1599.cpp @@ -0,0 +1,46 @@ +#include +using namespace std; +#define int long long +#define endl "\n" +const int INF = 0x3f3f3f3f; +const int N = 110; + +int mp[N][N], dis[N][N]; +int n, m, a, b, c, ans; + +void floyd() { + for (int k = 1; k <= n; k++) { + for (int i = 1; i < k; i++) // 枚举ij + for (int j = i + 1; j < k; j++) // 注意ijk不能相同 + if (ans > mp[i][j] + dis[j][k] + dis[k][i]) + ans = mp[i][j] + dis[j][k] + dis[k][i]; + + for (int i = 1; i <= n; i++) // 原floyd + for (int j = 1; j <= n; j++) + if (mp[i][j] > mp[i][k] + mp[k][j]) + mp[i][j] = mp[i][k] + mp[k][j]; + } +} +signed main() { + while (cin >> n >> m && (~n && ~m)) { + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + if (i == j) + mp[i][j] = dis[i][j] = 0; + else + mp[i][j] = dis[i][j] = INF; + while (m--) { + cin >> a >> b >> c; + if (c < mp[a][b]) // 防重边,怕了怕了 + mp[a][b] = dis[a][b] = c; + if (c < mp[b][a]) + mp[b][a] = dis[b][a] = c; + } + ans = INF; + floyd(); + if (ans == INF) + puts("It's impossible."); + else + printf("%lld\n", ans); + } +} \ No newline at end of file diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 63860a7..457fec8 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -190,7 +190,35 @@ int main() { #### [$HDU$-$1599$ $find$ $the$ $mincost$ $route$](https://acm.hdu.edu.cn/showproblem.php?pid=1599) -(最小环) + +**类型: 最小环** + +**题意**: +>杭州有$N$个景区,景区之间有一些双向的路来连接,现在$8600$想找一条旅游路线,这个路线从$A$点出发并且最后回到$A$点,假设经过的路线为$V_1,V_2,…V_K$,$V_1$,那么必须满足$K>2$,就是说至除了出发点以外至少要经过$2$个其他不同的景区,而且不能重复经过同一个景区。现在$8600$需要你帮他找一条这样的路线,并且花费越少越好。 +>**$Input$** +第一行是$2$个整数$N$和$M$($N <= 100, M <= 1000$),代表景区的个数和道路的条数。 +接下来的$M$行里,每行包括$3$个整数$a,b,c$.代表$a$和$b$之间有一条通路,并且需要花费$c$元($c <= 100$)。 +**$Output$** +对于每个测试实例,如果能找到这样一条路线的话,输出花费的最小值。如果找不到的话,输出"It’s impossible.". +**$Sample$ $Input$** +cpp +3 3 +1 2 1 +2 3 1 +1 3 1 +3 3 +1 2 1 +1 2 3 +2 3 1 +**$Sample$ $Output$** +3 +It’s impossible + +**分析**: +求最小环,用$dis[]$记录原距离,当枚举中间结点 $k$时,首先知道任意两点 $i、j$不经过 $k$的最短路径 $mp[i][j]$(原$floyd$的二三重循环后更新 $mp[i][j]$得到经过 $k$的最短路),此时枚举 $i$和 $j$得到一个经过 $k$的环( $i$到 $j$, $j$到 $k$, $k$到 $i$)并记录最小答案即可,即 $mp[i][j] + dis[j][k] + dis[k][i]$。 +注意题目 $i, j, k$不能相同,还有坑点:`long long` + + #### [$HDU$-$1704$ $Rank$](https://acm.hdu.edu.cn/showproblem.php?pid=1704) (传递闭包) From 84b85fb3c578ddc94bcf283465b6d4dfbe6b844e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 09:23:44 +0800 Subject: [PATCH 75/87] 'commit' --- TangDou/Topic/HDU1599.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/TangDou/Topic/HDU1599.cpp b/TangDou/Topic/HDU1599.cpp index d5f2ddd..e5bddb3 100644 --- a/TangDou/Topic/HDU1599.cpp +++ b/TangDou/Topic/HDU1599.cpp @@ -5,20 +5,20 @@ using namespace std; const int INF = 0x3f3f3f3f; const int N = 110; -int mp[N][N], dis[N][N]; +int dis[N][N], g[N][N]; int n, m, a, b, c, ans; void floyd() { for (int k = 1; k <= n; k++) { for (int i = 1; i < k; i++) // 枚举ij for (int j = i + 1; j < k; j++) // 注意ijk不能相同 - if (ans > mp[i][j] + dis[j][k] + dis[k][i]) - ans = mp[i][j] + dis[j][k] + dis[k][i]; + if (ans > dis[i][j] + g[j][k] + g[k][i]) + ans = dis[i][j] + g[j][k] + g[k][i]; for (int i = 1; i <= n; i++) // 原floyd for (int j = 1; j <= n; j++) - if (mp[i][j] > mp[i][k] + mp[k][j]) - mp[i][j] = mp[i][k] + mp[k][j]; + if (dis[i][j] > dis[i][k] + dis[k][j]) + dis[i][j] = dis[i][k] + dis[k][j]; } } signed main() { @@ -26,15 +26,15 @@ signed main() { for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (i == j) - mp[i][j] = dis[i][j] = 0; + dis[i][j] = g[i][j] = 0; else - mp[i][j] = dis[i][j] = INF; + dis[i][j] = g[i][j] = INF; while (m--) { cin >> a >> b >> c; - if (c < mp[a][b]) // 防重边,怕了怕了 - mp[a][b] = dis[a][b] = c; - if (c < mp[b][a]) - mp[b][a] = dis[b][a] = c; + if (c < dis[a][b]) // 防重边,怕了怕了 + dis[a][b] = g[a][b] = c; + if (c < dis[b][a]) + dis[b][a] = g[b][a] = c; } ans = INF; floyd(); From efdb5a1ad9589cfb43a8f4f9069955b9adbf3bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 09:30:19 +0800 Subject: [PATCH 76/87] 'commit' --- TangDou/Topic/HDU1599.cpp | 26 ++++++------ TangDou/Topic/【Floyd专题】.md | 63 ++++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 20 deletions(-) diff --git a/TangDou/Topic/HDU1599.cpp b/TangDou/Topic/HDU1599.cpp index e5bddb3..30345d9 100644 --- a/TangDou/Topic/HDU1599.cpp +++ b/TangDou/Topic/HDU1599.cpp @@ -6,14 +6,16 @@ const int INF = 0x3f3f3f3f; const int N = 110; int dis[N][N], g[N][N]; -int n, m, a, b, c, ans; +int n, m, ans; void floyd() { + memcpy(dis, g, sizeof g); for (int k = 1; k <= n; k++) { - for (int i = 1; i < k; i++) // 枚举ij - for (int j = i + 1; j < k; j++) // 注意ijk不能相同 - if (ans > dis[i][j] + g[j][k] + g[k][i]) - ans = dis[i][j] + g[j][k] + g[k][i]; + // 最小环的DP操作 + for (int i = 1; i < k; i++) // 枚举i,j + for (int j = i + 1; j < k; j++) // 注意i,j,k不能相同 + if (ans > dis[i][j] + g[i][k] + g[k][j]) + ans = dis[i][j] + g[i][k] + g[k][j]; for (int i = 1; i <= n; i++) // 原floyd for (int j = 1; j <= n; j++) @@ -23,24 +25,24 @@ void floyd() { } signed main() { while (cin >> n >> m && (~n && ~m)) { + // 邻接矩阵初始化 for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (i == j) - dis[i][j] = g[i][j] = 0; + g[i][j] = 0; else - dis[i][j] = g[i][j] = INF; + g[i][j] = INF; + while (m--) { + int a, b, c; cin >> a >> b >> c; - if (c < dis[a][b]) // 防重边,怕了怕了 - dis[a][b] = g[a][b] = c; - if (c < dis[b][a]) - dis[b][a] = g[b][a] = c; + g[a][b] = g[b][a] = min(c, g[a][b]); // 防重边 } ans = INF; floyd(); if (ans == INF) puts("It's impossible."); else - printf("%lld\n", ans); + cout << ans << endl; } } \ No newline at end of file diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 457fec8..5ce48c2 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -5,14 +5,6 @@ $Floyd$算法是一次性求所有结点之间的最短距离,能处理负权 $Floyd$运用了 **动态规划** 的思想,求 $i 、 j$两点的最短距离,可分两种情况考虑,即经过图中某个点 $k$的路径和不经过点 $k$ 的路径,**取两者中的最短路径**。 -- 判断负圈 -眼尖的人儿可能发现邻接矩阵 $mp$ 中, $mp[i][i]$并没有赋初值$0$,而是 $inf$。并且计算后 $mp[i][i]$的值也不是 $0$,而是 $mp[i][i]=mp[i][u]+……+mp[v][i]$,即从外面绕一圈回来的最短路径,而这正 **用于判断负圈**,即 $mp[i][i]<0$。 - -相关变形结合题目讲,如:负圈、打印路径、最小环、传递闭包 - -记录坑点:**重复边**,保留最小的那个。 - - ### 二、模板 ```cpp {.line-numbers} void floyd() { @@ -27,6 +19,12 @@ void floyd() { ### 三、判负环 +眼尖的人儿可能发现邻接矩阵 $g$ 中, $g[i][i]$并没有赋初值$0$,而是 $inf$。并且计算后 $g[i][i]$的值也不是 $0$,而是 $g[i][i]=g[i][u]+……+g[v][i]$,即从外面绕一圈回来的最短路径,而这正 **用于判断负圈**,即 $g[i][i]<0$。 + +相关变形结合题目讲,如:负圈、打印路径、最小环、传递闭包 + +记录坑点:**重复边**,保留最小的那个。 + #### [$POJ-3259$ $Wormholes$](https://link.juejin.cn/?target=https%3A%2F%2Fvjudge.net%2Fproblem%2FPOJ-3259) **类型** @@ -218,7 +216,56 @@ It’s impossible 求最小环,用$dis[]$记录原距离,当枚举中间结点 $k$时,首先知道任意两点 $i、j$不经过 $k$的最短路径 $mp[i][j]$(原$floyd$的二三重循环后更新 $mp[i][j]$得到经过 $k$的最短路),此时枚举 $i$和 $j$得到一个经过 $k$的环( $i$到 $j$, $j$到 $k$, $k$到 $i$)并记录最小答案即可,即 $mp[i][j] + dis[j][k] + dis[k][i]$。 注意题目 $i, j, k$不能相同,还有坑点:`long long` +```cpp {.line-numbers} +#include +using namespace std; +#define int long long +#define endl "\n" +const int INF = 0x3f3f3f3f; +const int N = 110; +int dis[N][N], g[N][N]; +int n, m, ans; + +void floyd() { + memcpy(dis, g, sizeof g); + for (int k = 1; k <= n; k++) { + // 最小环的DP操作 + for (int i = 1; i < k; i++) // 枚举i,j + for (int j = i + 1; j < k; j++) // 注意i,j,k不能相同 + if (ans > dis[i][j] + g[i][k] + g[k][j]) + ans = dis[i][j] + g[i][k] + g[k][j]; + + for (int i = 1; i <= n; i++) // 原floyd + for (int j = 1; j <= n; j++) + if (dis[i][j] > dis[i][k] + dis[k][j]) + dis[i][j] = dis[i][k] + dis[k][j]; + } +} +signed main() { + while (cin >> n >> m && (~n && ~m)) { + // 邻接矩阵初始化 + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + if (i == j) + g[i][j] = 0; + else + g[i][j] = INF; + + while (m--) { + int a, b, c; + cin >> a >> b >> c; + g[a][b] = g[b][a] = min(c, g[a][b]); // 防重边 + } + ans = INF; + floyd(); + if (ans == INF) + puts("It's impossible."); + else + cout << ans << endl; + } +} +``` #### [$HDU$-$1704$ $Rank$](https://acm.hdu.edu.cn/showproblem.php?pid=1704) (传递闭包) From 4377432f8362e841d8cdd12a931bc0fd45792f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 10:21:55 +0800 Subject: [PATCH 77/87] 'commit' --- TangDou/Topic/HDU1704.cpp | 36 +++++++++++++++++ TangDou/Topic/HDU3631.cpp | 50 +++++++++++++++++++++++ TangDou/Topic/【Floyd专题】.md | 65 +++++++++++++++++++++++++++--- 3 files changed, 145 insertions(+), 6 deletions(-) create mode 100644 TangDou/Topic/HDU1704.cpp create mode 100644 TangDou/Topic/HDU3631.cpp diff --git a/TangDou/Topic/HDU1704.cpp b/TangDou/Topic/HDU1704.cpp new file mode 100644 index 0000000..e8872fb --- /dev/null +++ b/TangDou/Topic/HDU1704.cpp @@ -0,0 +1,36 @@ +#include +using namespace std; +#define inf 0x3f3f3f3f +const int N = 510; +int n, m, x, y, ans; +int g[N][N]; + +void floyd() { + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) { + if (!g[i][k]) continue; // floyd优化 + for (int j = 1; j <= n; j++) + g[i][j] |= g[i][k] & g[k][j]; // 通过k传递,或运算 + } +} +int main() { + int T; + cin >> T; + while (T--) { + cin >> n >> m; + memset(g, 0, sizeof g); + while (m--) { + cin >> x >> y; + g[x][y] = 1; // x +using namespace std; +#define inf 0x3f3f3f3f +const int N = 310; +int t, n, m, q, u, v, w; +int mp[N][N]; +bool flag[N]; // 记录是否标记 + +void floyd(int k) { + for (int i = 0; i < n; ++i) + for (int j = 0; j < n; ++j) + if (mp[i][j] > mp[i][k] + mp[k][j]) + mp[i][j] = mp[i][k] + mp[k][j]; +} +int main() { + while (cin >> n >> m >> q && n + m + q) { + if (t != 0) printf("\n"); // 谜之格式 + printf("Case %d:\n", ++t); + memset(mp, inf, sizeof(mp)); + memset(flag, false, sizeof(flag)); + for (int i = 0; i <= n; ++i) + mp[i][i] = 0; + while (m--) { + scanf("%d %d %d", &u, &v, &w); + if (w < mp[u][v]) + mp[u][v] = w; + } + while (q--) { + scanf("%d", &w); + if (w == 0) { + scanf("%d", &u); + if (flag[u]) + printf("ERROR! At point %d\n", u); + else { + flag[u] = true; + floyd(u); + } + } else { + scanf("%d %d", &u, &v); + if (!(flag[u] && flag[v])) + printf("ERROR! At path %d to %d\n", u, v); + else if (mp[u][v] == inf) + printf("No such path\n"); + else + printf("%d\n", mp[u][v]); + } + } + } + return 0; +} \ No newline at end of file diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 5ce48c2..e1ea57c 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -95,7 +95,7 @@ int main() { } ``` -### 四、练习题 +### 四、打印路径 #### [$HDU-1385$ $Minimum$ $Transport$ $Cost$](http://acm.hdu.edu.cn/showproblem.php?pid=1385) @@ -186,7 +186,7 @@ int main() { } ``` - +### 五、最小环 #### [$HDU$-$1599$ $find$ $the$ $mincost$ $route$](https://acm.hdu.edu.cn/showproblem.php?pid=1599) **类型: 最小环** @@ -266,14 +266,67 @@ signed main() { } } ``` - +### 六、传递闭包 #### [$HDU$-$1704$ $Rank$](https://acm.hdu.edu.cn/showproblem.php?pid=1704) -(传递闭包) + +**题意** +给出$M$对胜负关系,胜负关系有传递性(若$A$胜$B$,$B$胜$C$则$A$胜$C$), **求有多少对不能确定的胜负关系** + +**解法**:思路很简单,$floyd$ 一遍做传递闭包,然后暴力枚举就行辣,但是竟然会$TLE$,然后上网学了一种新的优化姿势(其实这种优化用处不大,但由于本题是非常稀疏的图,所以$O(N^3)$几乎变成了$O(N^2)$) + +```cpp {.line-numbers} +#include +using namespace std; +#define inf 0x3f3f3f3f +const int N = 510; +int n, m, x, y, ans; +int g[N][N]; + +void floyd() { + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) { + if (!g[i][k]) continue; // floyd优化 + for (int j = 1; j <= n; j++) + g[i][j] |= g[i][k] & g[k][j]; // 通过k传递,或运算 + } +} +int main() { + int T; + cin >> T; + while (T--) { + cin >> n >> m; + memset(g, 0, sizeof g); + while (m--) { + cin >> x >> y; + g[x][y] = 1; // x Date: Fri, 5 Jan 2024 10:24:28 +0800 Subject: [PATCH 78/87] 'commit' --- TangDou/Topic/HDU3631.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TangDou/Topic/HDU3631.cpp b/TangDou/Topic/HDU3631.cpp index 16ae0d4..c9ac698 100644 --- a/TangDou/Topic/HDU3631.cpp +++ b/TangDou/Topic/HDU3631.cpp @@ -14,10 +14,10 @@ void floyd(int k) { } int main() { while (cin >> n >> m >> q && n + m + q) { - if (t != 0) printf("\n"); // 谜之格式 + if (t) printf("\n"); // 谜之格式 printf("Case %d:\n", ++t); - memset(mp, inf, sizeof(mp)); - memset(flag, false, sizeof(flag)); + memset(mp, inf, sizeof mp); + memset(flag, false, sizeof flag); for (int i = 0; i <= n; ++i) mp[i][i] = 0; while (m--) { From dcceeb072ee910591c51575de7f933f4542a8026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 10:25:59 +0800 Subject: [PATCH 79/87] 'commit' --- TangDou/Topic/HDU3631.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/TangDou/Topic/HDU3631.cpp b/TangDou/Topic/HDU3631.cpp index c9ac698..8a46f47 100644 --- a/TangDou/Topic/HDU3631.cpp +++ b/TangDou/Topic/HDU3631.cpp @@ -13,22 +13,22 @@ void floyd(int k) { mp[i][j] = mp[i][k] + mp[k][j]; } int main() { + // 加快读入 + ios::sync_with_stdio(false), cin.tie(0); while (cin >> n >> m >> q && n + m + q) { if (t) printf("\n"); // 谜之格式 printf("Case %d:\n", ++t); memset(mp, inf, sizeof mp); memset(flag, false, sizeof flag); - for (int i = 0; i <= n; ++i) - mp[i][i] = 0; + for (int i = 0; i <= n; i++) mp[i][i] = 0; while (m--) { - scanf("%d %d %d", &u, &v, &w); - if (w < mp[u][v]) - mp[u][v] = w; + cin >> u >> v >> w; + if (w < mp[u][v]) mp[u][v] = w; } while (q--) { - scanf("%d", &w); + cin >> w; if (w == 0) { - scanf("%d", &u); + cin >> u; if (flag[u]) printf("ERROR! At point %d\n", u); else { @@ -36,7 +36,7 @@ int main() { floyd(u); } } else { - scanf("%d %d", &u, &v); + cin >> u >> v; if (!(flag[u] && flag[v])) printf("ERROR! At path %d to %d\n", u, v); else if (mp[u][v] == inf) From 40e4ca2839027ea27b7843259ce5d702e741464e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 11:00:56 +0800 Subject: [PATCH 80/87] 'commit' --- TangDou/Topic/HDU3631.cpp | 52 +++++++++++++----------- TangDou/Topic/【Floyd专题】.md | 63 ++++++++++++++++++++++++++---- 2 files changed, 85 insertions(+), 30 deletions(-) diff --git a/TangDou/Topic/HDU3631.cpp b/TangDou/Topic/HDU3631.cpp index 8a46f47..5c768a6 100644 --- a/TangDou/Topic/HDU3631.cpp +++ b/TangDou/Topic/HDU3631.cpp @@ -2,47 +2,53 @@ using namespace std; #define inf 0x3f3f3f3f const int N = 310; -int t, n, m, q, u, v, w; -int mp[N][N]; +int t, n, m, q; +int g[N][N]; bool flag[N]; // 记录是否标记 +int a, b, c; -void floyd(int k) { - for (int i = 0; i < n; ++i) - for (int j = 0; j < n; ++j) - if (mp[i][j] > mp[i][k] + mp[k][j]) - mp[i][j] = mp[i][k] + mp[k][j]; +void floyd(int k) { // 以k为中转节点进行转移 + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + if (g[i][j] > g[i][k] + g[k][j]) + g[i][j] = g[i][k] + g[k][j]; } + int main() { // 加快读入 ios::sync_with_stdio(false), cin.tie(0); while (cin >> n >> m >> q && n + m + q) { if (t) printf("\n"); // 谜之格式 printf("Case %d:\n", ++t); - memset(mp, inf, sizeof mp); + + // 整体正无穷,对角线清零 + memset(g, inf, sizeof g); + for (int i = 0; i <= n; i++) g[i][i] = 0; + memset(flag, false, sizeof flag); - for (int i = 0; i <= n; i++) mp[i][i] = 0; + while (m--) { - cin >> u >> v >> w; - if (w < mp[u][v]) mp[u][v] = w; + cin >> a >> b >> c; + g[a][b] = min(c, g[a][b]); // floyd也可以跑有向图 } while (q--) { - cin >> w; - if (w == 0) { - cin >> u; - if (flag[u]) - printf("ERROR! At point %d\n", u); + cin >> c; + if (c == 0) { + cin >> a; + if (flag[a]) // 如果a已经被标记过了 + printf("ERROR! At point %d\n", a); else { - flag[u] = true; - floyd(u); + flag[a] = true; // 标记上 + floyd(a); // 通过a进行其它节点转移 } } else { - cin >> u >> v; - if (!(flag[u] && flag[v])) - printf("ERROR! At path %d to %d\n", u, v); - else if (mp[u][v] == inf) + cin >> a >> b; + if (!(flag[a] && flag[b])) + printf("ERROR! At path %d to %d\n", a, b); + else if (g[a][b] == inf) printf("No such path\n"); else - printf("%d\n", mp[u][v]); + printf("%d\n", g[a][b]); } } } diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index e1ea57c..825828d 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -21,10 +21,6 @@ void floyd() { 眼尖的人儿可能发现邻接矩阵 $g$ 中, $g[i][i]$并没有赋初值$0$,而是 $inf$。并且计算后 $g[i][i]$的值也不是 $0$,而是 $g[i][i]=g[i][u]+……+g[v][i]$,即从外面绕一圈回来的最短路径,而这正 **用于判断负圈**,即 $g[i][i]<0$。 -相关变形结合题目讲,如:负圈、打印路径、最小环、传递闭包 - -记录坑点:**重复边**,保留最小的那个。 - #### [$POJ-3259$ $Wormholes$](https://link.juejin.cn/?target=https%3A%2F%2Fvjudge.net%2Fproblem%2FPOJ-3259) **类型** @@ -213,7 +209,7 @@ cpp It’s impossible **分析**: -求最小环,用$dis[]$记录原距离,当枚举中间结点 $k$时,首先知道任意两点 $i、j$不经过 $k$的最短路径 $mp[i][j]$(原$floyd$的二三重循环后更新 $mp[i][j]$得到经过 $k$的最短路),此时枚举 $i$和 $j$得到一个经过 $k$的环( $i$到 $j$, $j$到 $k$, $k$到 $i$)并记录最小答案即可,即 $mp[i][j] + dis[j][k] + dis[k][i]$。 +求最小环,用$g[]$记录原距离,当枚举中间结点 $k$时,首先知道任意两点 $i、j$不经过 $k$的最短路径 $dis[i][j]$(原$floyd$的二三重循环后更新 $dis[i][j]$得到经过$k$的最短路),此时枚举 $i$和 $j$得到一个经过 $k$的环( $i$到 $j$, $j$到 $k$, $k$到 $i$)并记录最小答案即可,即 $dis[i][j] + g[i][k] + g[k][j]$。 注意题目 $i, j, k$不能相同,还有坑点:`long long` ```cpp {.line-numbers} @@ -317,7 +313,6 @@ int main() { ### 七、变形 #### [$HDU$-$3631$ $Shortest$ $Path$](https://acm.hdu.edu.cn/showproblem.php?pid=3631) -(变形) **题意** 有向图求$2$点间的最短路径,要求只能经过被标记的点 @@ -326,7 +321,61 @@ int main() { 由于只能用标记的点去更新,并且又要求任意两点之间的最短距离,显然$floyd$是最合适的。 这道题要用$floyd$过的话关键就看对于$floyd$的理解了,因为只有标记的点可以走,为了节省时间,我们可以在新标记点的时候以那点为中转点进行一次$floyd$,这就避免了$n^3$的复杂度 +```cpp {.line-numbers} +#include +using namespace std; +#define inf 0x3f3f3f3f +const int N = 310; +int t, n, m, q; +int g[N][N]; +bool flag[N]; // 记录是否标记 +int a, b, c; + +void floyd(int k) { // 以k为中转节点进行转移 + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + if (g[i][j] > g[i][k] + g[k][j]) + g[i][j] = g[i][k] + g[k][j]; +} + +int main() { + // 加快读入 + ios::sync_with_stdio(false), cin.tie(0); + while (cin >> n >> m >> q && n + m + q) { + if (t) printf("\n"); // 谜之格式 + printf("Case %d:\n", ++t); + // 整体正无穷,对角线清零 + memset(g, inf, sizeof g); + for (int i = 0; i <= n; i++) g[i][i] = 0; + memset(flag, false, sizeof flag); -https://juejin.cn/post/6935691567696969764 \ No newline at end of file + while (m--) { + cin >> a >> b >> c; + g[a][b] = min(c, g[a][b]); // floyd也可以跑有向图 + } + while (q--) { + cin >> c; + if (c == 0) { + cin >> a; + if (flag[a]) // 如果a已经被标记过了 + printf("ERROR! At point %d\n", a); + else { + flag[a] = true; // 标记上 + floyd(a); // 通过a进行其它节点转移 + } + } else { + cin >> a >> b; + if (!(flag[a] && flag[b])) + printf("ERROR! At path %d to %d\n", a, b); + else if (g[a][b] == inf) + printf("No such path\n"); + else + printf("%d\n", g[a][b]); + } + } + } + return 0; +} +``` \ No newline at end of file From 311747d5423c510c524bc47e95f65a5a7b75ff93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 11:27:50 +0800 Subject: [PATCH 81/87] 'commit' --- TangDou/Topic/P1828.cpp | 38 +++++++++++++++ TangDou/Topic/SSL_1760.cpp | 29 ++++++++++++ TangDou/Topic/【Floyd专题】.md | 75 +++++++++++++++++++++++++++++- 3 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 TangDou/Topic/P1828.cpp create mode 100644 TangDou/Topic/SSL_1760.cpp diff --git a/TangDou/Topic/P1828.cpp b/TangDou/Topic/P1828.cpp new file mode 100644 index 0000000..640ea05 --- /dev/null +++ b/TangDou/Topic/P1828.cpp @@ -0,0 +1,38 @@ +#include +using namespace std; +const int N = 814; +const int INF = 0x3f3f3f3f; +int id[N]; +int a, b, c, g[N][N], res = INF; +int main() { + // 加快读入 + ios::sync_with_stdio(false), cin.tie(0); + int p, n, m; // p只奶牛,n个牧场,m条边 + cin >> p >> n >> m; + + memset(g, 0x3f, sizeof g); + for (int i = 1; i <= n; i++) g[i][i] = 0; // 初始化 + + for (int i = 1; i <= p; i++) cin >> id[i]; // i号奶牛,在id[i]这个牧场 + + while (m--) { + cin >> a >> b >> c; + g[a][b] = g[b][a] = min(c, g[a][b]); + } + // 标准的Floyd板子 + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) { + if (g[i][k] == INF) continue; // floyd小优化 + for (int j = 1; j <= n; j++) + if (g[i][j] > g[i][k] + g[k][j]) + g[j][i] = g[i][j] = g[i][k] + g[k][j]; + } + + for (int i = 1; i <= n; i++) { // 每个牧场出发 + int ans = 0; + for (int j = 1; j <= p; j++) ans += g[i][id[j]]; + res = min(res, ans); + } + printf("%d", res); + return 0; +} diff --git a/TangDou/Topic/SSL_1760.cpp b/TangDou/Topic/SSL_1760.cpp new file mode 100644 index 0000000..65d006d --- /dev/null +++ b/TangDou/Topic/SSL_1760.cpp @@ -0,0 +1,29 @@ +#include +using namespace std; +const int N = 210; +int n, g[N][N]; +const int INF = 0x3f3f3f3f; +int ans = INF; +int main() { + cin >> n; + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) { + cin >> g[i][j]; + if (g[i][j] == 0 && i != j) g[i][j] = INF; // 建图(注意i==j要为0) + } + // floyd + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + if (g[i][j] > g[i][k] + g[k][j]) + g[i][j] = g[i][k] + g[k][j]; + + int s; + for (int i = 1; i <= n; i++) { + s = 0; + for (int j = 1; j <= n; j++) s += g[i][j]; + if (s < ans) ans = s; + } + printf("%d", ans); + return 0; +} diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 825828d..d0ab3ac 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -378,4 +378,77 @@ int main() { } return 0; } -``` \ No newline at end of file +``` + +### 八、其它习题 + +#### $SSL-1760$(商店选址) +**题目** +给出一个城市的地图(用邻接矩阵表示),商店设在一点,使各个地方到商店距离之和最短。 + +$Input$ +第一行为$n$(共有几个城市); $N$小于$201$ +第二行至第$n+1$行为城市地图(用邻接矩阵表示) + +$Output$ +最短路径之和 + +$Sample$ $Input$ +```cpp {.line-numbers} +3 +0 3 1 +3 0 2 +1 2 0 +1 +2 +3 +4 +``` + +$Sample$ $Output$ +```cpp {.line-numbers} +3 +1 +``` +```cpp {.line-numbers} +#include +using namespace std; +const int N = 210; +int n, g[N][N]; +const int INF = 0x3f3f3f3f; +int ans = INF; +int main() { + cin >> n; + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) { + cin >> g[i][j]; + if (g[i][j] == 0 && i != j) g[i][j] = INF; // 建图(注意i==j要为0) + } + // floyd + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + if (g[i][j] > g[i][k] + g[k][j]) + g[i][j] = g[i][k] + g[k][j]; + + int s; + for (int i = 1; i <= n; i++) { + s = 0; + for (int j = 1; j <= n; j++) s += g[i][j]; + if (s < ans) ans = s; + } + printf("%d", ans); + return 0; +} + +``` +#### [$P1828$ [$USACO3.2$] 香甜的黄油 $Sweet$ $Butter$](https://www.luogu.com.cn/problem/P1828) + + +SSL-1761(城市问题) + +#### [$P1364$ 医院设置](https://www.luogu.com.cn/problem/P1364) + +SSL-1613=CCF1342(最短路径问题) + + From 044e161f678f7e2cab718b41af21c525c05000a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 11:31:32 +0800 Subject: [PATCH 82/87] 'commit' --- TangDou/Topic/P1828.cpp | 6 +++- TangDou/Topic/P1828_11.in | 9 ++++++ TangDou/Topic/【Floyd专题】.md | 44 ++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 TangDou/Topic/P1828_11.in diff --git a/TangDou/Topic/P1828.cpp b/TangDou/Topic/P1828.cpp index 640ea05..80fb5f9 100644 --- a/TangDou/Topic/P1828.cpp +++ b/TangDou/Topic/P1828.cpp @@ -5,6 +5,10 @@ const int INF = 0x3f3f3f3f; int id[N]; int a, b, c, g[N][N], res = INF; int main() { +#ifndef ONLINE_JUDGE + freopen("P1828_11.in", "r", stdin); + // 参考答案:8 +#endif // 加快读入 ios::sync_with_stdio(false), cin.tie(0); int p, n, m; // p只奶牛,n个牧场,m条边 @@ -31,7 +35,7 @@ int main() { for (int i = 1; i <= n; i++) { // 每个牧场出发 int ans = 0; for (int j = 1; j <= p; j++) ans += g[i][id[j]]; - res = min(res, ans); + if (ans >= 0) res = min(res, ans); } printf("%d", res); return 0; diff --git a/TangDou/Topic/P1828_11.in b/TangDou/Topic/P1828_11.in new file mode 100644 index 0000000..aea7f2f --- /dev/null +++ b/TangDou/Topic/P1828_11.in @@ -0,0 +1,9 @@ +3 5 5 +2 +3 +4 +1 2 1 +1 3 5 +2 3 7 +2 4 3 +3 4 5 diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index d0ab3ac..f760c7a 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -443,7 +443,51 @@ int main() { ``` #### [$P1828$ [$USACO3.2$] 香甜的黄油 $Sweet$ $Butter$](https://www.luogu.com.cn/problem/P1828) +```cpp {.line-numbers} +#include +using namespace std; +const int N = 814; +const int INF = 0x3f3f3f3f; +int id[N]; +int a, b, c, g[N][N], res = INF; +int main() { +#ifndef ONLINE_JUDGE + freopen("P1828_11.in", "r", stdin); + // 参考答案:8 +#endif + // 加快读入 + ios::sync_with_stdio(false), cin.tie(0); + int p, n, m; // p只奶牛,n个牧场,m条边 + cin >> p >> n >> m; + memset(g, 0x3f, sizeof g); + for (int i = 1; i <= n; i++) g[i][i] = 0; // 初始化 + + for (int i = 1; i <= p; i++) cin >> id[i]; // i号奶牛,在id[i]这个牧场 + + while (m--) { + cin >> a >> b >> c; + g[a][b] = g[b][a] = min(c, g[a][b]); + } + // 标准的Floyd板子 + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) { + if (g[i][k] == INF) continue; // floyd小优化 + for (int j = 1; j <= n; j++) + if (g[i][j] > g[i][k] + g[k][j]) + g[j][i] = g[i][j] = g[i][k] + g[k][j]; + } + + for (int i = 1; i <= n; i++) { // 每个牧场出发 + int ans = 0; + for (int j = 1; j <= p; j++) ans += g[i][id[j]]; + if (ans >= 0) res = min(res, ans); + } + printf("%d", res); + return 0; +} + +``` SSL-1761(城市问题) From f50e50fbc526b6713cff12c48c004bd4c82294bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 11:43:02 +0800 Subject: [PATCH 83/87] 'commit' --- TangDou/Topic/P1364.cpp | 38 ++++++++++++++++++++++++++ TangDou/Topic/【Floyd专题】.md | 44 ++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 TangDou/Topic/P1364.cpp diff --git a/TangDou/Topic/P1364.cpp b/TangDou/Topic/P1364.cpp new file mode 100644 index 0000000..4cd6375 --- /dev/null +++ b/TangDou/Topic/P1364.cpp @@ -0,0 +1,38 @@ +#include +using namespace std; +const int N = 1000010; +const int INF = 0x3f3f3f3f; + +int g[150][150]; +int w[N]; // 居民人口数,点权 + +int main() { + int n; + cin >> n; + + // 地图初始化 + memset(g, 0x3f, sizeof g); + for (int i = 1; i <= n; i++) g[i][i] = 0; + + for (int i = 1; i <= n; i++) { + int a, b; + cin >> w[i] >> a >> b; // w[i]:点权 + g[i][a] = g[a][i] = 1; // i<->a无向边 + g[i][b] = g[b][i] = 1; // i<->b无向边 + } + + // floyd + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + if (g[i][j] > g[i][k] + g[k][j]) g[i][j] = g[i][k] + g[k][j]; + + int ans = INF; + for (int i = 1; i <= n; i++) { // 如果将医院设置在i处,那么计算一下它到各地的加权和 + int s = 0; + for (int j = 1; j <= n; j++) s += w[j] * g[i][j]; + ans = min(ans, s); + } + printf("%d", ans); + return 0; +} \ No newline at end of file diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index f760c7a..bf389d1 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -489,10 +489,50 @@ int main() { ``` -SSL-1761(城市问题) - #### [$P1364$ 医院设置](https://www.luogu.com.cn/problem/P1364) +```cpp {.line-numbers} +#include +using namespace std; +const int N = 1000010; +const int INF = 0x3f3f3f3f; + +int g[150][150]; +int w[N]; // 居民人口数,点权 + +int main() { + int n; + cin >> n; + + // 地图初始化 + memset(g, 0x3f, sizeof g); + for (int i = 1; i <= n; i++) g[i][i] = 0; + + for (int i = 1; i <= n; i++) { + int a, b; + cin >> w[i] >> a >> b; // w[i]:点权 + g[i][a] = g[a][i] = 1; // i<->a无向边 + g[i][b] = g[b][i] = 1; // i<->b无向边 + } + + // floyd + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + if (g[i][j] > g[i][k] + g[k][j]) g[i][j] = g[i][k] + g[k][j]; + + int ans = INF; + for (int i = 1; i <= n; i++) { // 如果将医院设置在i处,那么计算一下它到各地的加权和 + int s = 0; + for (int j = 1; j <= n; j++) s += w[j] * g[i][j]; + ans = min(ans, s); + } + printf("%d", ans); + return 0; +} +``` + SSL-1613=CCF1342(最短路径问题) +SSL-1761(城市问题) From 7239d0a40a920ff3a4df8c1b8dc12adf8c06eb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 15:10:48 +0800 Subject: [PATCH 84/87] 'commit' --- TangDou/Topic/SSL1613.cpp | 43 +++++++++++++++ TangDou/Topic/【Floyd专题】.md | 84 +++++++++++++++++++++++++++++- 2 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 TangDou/Topic/SSL1613.cpp diff --git a/TangDou/Topic/SSL1613.cpp b/TangDou/Topic/SSL1613.cpp new file mode 100644 index 0000000..5d667dc --- /dev/null +++ b/TangDou/Topic/SSL1613.cpp @@ -0,0 +1,43 @@ +#include +using namespace std; +int o(int t) { + return t * t; +} +const int N = 110; +int n, m, x[N], y[N]; +double g[N][N]; + +int main() { + cin >> n; + for (int i = 1; i <= n; i++) cin >> x[i] >> y[i]; + + // double类型的数组,初始化不能用memset!!!! + // memset(g, 0x3f, sizeof g); + // for (int i = 0; i <= n; i++) g[i][i] = 0; + + // 需要用二重循环进行初始化 + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) { + if (i == j) + g[i][j] = 0; + else + g[i][j] = 0x3f3f3f3f; + } + + cin >> m; + int l, r; + while (m--) { + cin >> l >> r; + g[r][l] = g[l][r] = sqrt((double)o(x[l] - x[r]) + (double)o(y[l] - y[r])); // 勾股定理 + } + + // floyd + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + if (g[i][j] > g[i][k] + g[k][j]) g[i][j] = g[i][k] + g[k][j]; + + cin >> l >> r; + printf("%.2lf", g[l][r]); + return 0; +} diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index bf389d1..2843186 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -532,7 +532,87 @@ int main() { } ``` -SSL-1613=CCF1342(最短路径问题) -SSL-1761(城市问题) +#### [$SSL-1613$]() +> Description +平面上有$n$个点($N<=100$),每个点的坐标均在$-10000\sim 10000$之间。其中的一些点之间有连线。若有连线,则表示可从一个点到达另一个点,即两点间有通路,通路的距离为两点直线的 **距离** 。现在的任务是找出从一点到另一点之间的最短路径。 +$Input$ +输入文件$short.in$,共有$n+m+3$行,其中: +第一行为一个整数$n$。 +第$2$行到第$n+1$行(共$n$行),每行的两个整数$x$和$y$,描述一个点的坐标(以一个空格隔开)。 + +第$n+2$行为一个整数$m$,表示图中的连线个数。 +此后的$m$行,每行描述一条连线,由两个整数$i,j$组成,表示第$i$个点和第$j$个点之间有连线。 +最后一行:两个整数$s$和$t$,分别表示源点和目标点。 + +$Output$ +输出文件$short.out$仅一行,一个实数(保留两位小数),表示从$S$到$T$的最短路径的长度。 + +$Sample$ $Input$ +```cpp {.line-numbers} +5 +0 0 +2 0 +2 2 +0 2 +3 1 +5 +1 2 +1 3 +1 4 +2 5 +3 5 +1 5 +``` + +Sample Output +```cpp {.line-numbers} +3.41 +``` + +#### $Code$ +```cpp {.line-numbers} +#include +using namespace std; +int o(int t) { + return t * t; +} +const int N = 110; +int n, m, x[N], y[N]; +double g[N][N]; + +int main() { + cin >> n; + for (int i = 1; i <= n; i++) cin >> x[i] >> y[i]; + + // double类型的数组,初始化不能用memset!!!! + // memset(g, 0x3f, sizeof g); + // for (int i = 0; i <= n; i++) g[i][i] = 0; + + // 需要用二重循环进行初始化 + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) { + if (i == j) + g[i][j] = 0; + else + g[i][j] = 0x3f3f3f3f; + } + + cin >> m; + int l, r; + while (m--) { + cin >> l >> r; + g[r][l] = g[l][r] = sqrt((double)o(x[l] - x[r]) + (double)o(y[l] - y[r])); // 勾股定理 + } + // floyd + for (int k = 1; k <= n; k++) + for (int i = 1; i <= n; i++) + for (int j = 1; j <= n; j++) + if (g[i][j] > g[i][k] + g[k][j]) g[i][j] = g[i][k] + g[k][j]; + + cin >> l >> r; + printf("%.2lf", g[l][r]); + return 0; +} +``` \ No newline at end of file From f5ec0fc85af935f783fb21c2d2c3d9b6b302ef41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 15:24:10 +0800 Subject: [PATCH 85/87] 'commit' --- TangDou/Topic/【Floyd专题】.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 2843186..9c9ec72 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -17,6 +17,16 @@ void floyd() { } ``` +### 三、最短路+思维 + +#### [$AcWing$ $1125$ 牛的旅行](https://www.cnblogs.com/littlehb/p/16025700.html) + +总结: +- 一遍$floyd$算出原始各连通块内的多源最短路径 +- 遍历枚举找出每个点在自己连通块中可以到达的最远距离,$PK$后获取到原始的最大直径长度 +- 遍历所有可能连接上的两个不在同一连通块中的点,尝试连接上这两个点后,得到可以获得到的最小直径。 +- 原始直径与遍历尝试的所有可能直径$PK$,谁大谁是答案。 + ### 三、判负环 眼尖的人儿可能发现邻接矩阵 $g$ 中, $g[i][i]$并没有赋初值$0$,而是 $inf$。并且计算后 $g[i][i]$的值也不是 $0$,而是 $g[i][i]=g[i][u]+……+g[v][i]$,即从外面绕一圈回来的最短路径,而这正 **用于判断负圈**,即 $g[i][i]<0$。 From 1288403c648f229c776501a3be11049cb389965a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 16:01:19 +0800 Subject: [PATCH 86/87] 'commit' --- .../T3/Floyd/{344_1.cpp => 344.cpp} | 0 TangDou/AcWing_TiGao/T3/Floyd/344.md | 188 ++++++++---------- TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp | 70 ------- TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp | 70 ------- TangDou/Topic/【Floyd专题】.md | 16 +- 5 files changed, 96 insertions(+), 248 deletions(-) rename TangDou/AcWing_TiGao/T3/Floyd/{344_1.cpp => 344.cpp} (100%) delete mode 100644 TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp delete mode 100644 TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344_1.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344.cpp similarity index 100% rename from TangDou/AcWing_TiGao/T3/Floyd/344_1.cpp rename to TangDou/AcWing_TiGao/T3/Floyd/344.cpp diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344.md b/TangDou/AcWing_TiGao/T3/Floyd/344.md index 18ad9d4..15b72de 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/344.md +++ b/TangDou/AcWing_TiGao/T3/Floyd/344.md @@ -1,7 +1,7 @@ ## [$AcWing$ $344$. 观光之旅](https://www.acwing.com/problem/content/346/) ### 一、题目描述 -给定一张无向图,求图中一个 **至少包含 $3$ 个点** 的环,环上的节点不重复,并且环上的边的长度之和最小。 +给定一张无向图,求图中一个至少包含 $3$ 个点的环,环上的节点不重复,并且环上的边的长度之和最小。 该问题称为 **无向图的最小环问题**。 @@ -15,154 +15,138 @@ **输出格式** 输出占一行,包含最小环的所有节点(按顺序输出),如果不存在则输出 `No solution.`。 -**数据范围** -$1≤N≤100,1≤M≤10000,1≤l<500$ +### 二、$floyd + dp$求最小环模板题 -**输入样例**: -```cpp {.line-numbers} -5 7 -1 4 1 -1 3 300 -3 1 10 -1 2 16 -2 3 100 -2 5 15 -5 3 20 -``` - -**输出样例**: -```cpp {.line-numbers} -1 3 5 2 -``` +**最优化问题**,**从集合角度考虑($DP$)**,**将所有环按编号最大的点** 分成 $n$ 类,**求出每类最小**,最后在类间取 $min$ -### 二、算法思路 +分类的标准是 **可重、不漏**。(对于求数量的问题,分类的标准是 **不重不漏**) -> 环上的节点不重复,并且环上的边的长度之和最小。 -**解释**: -![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401031636536.png) -> 如果存在一个环,则上图中$k$出现多次,那么,如果去掉$k$身上的那个环,$a \rightarrow k \rightarrow b \rightarrow a $这个环的长度肯定是最小的。 -最优化问题,可以从集合角度来思考,从集合角度来思考的一个好处就是:不容易丢东西。 +#### 集合划分 +![](https://cdn.acwing.com/media/article/image/2021/08/08/52559_50494c4bf7-%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE-2021-08-08-101518.jpg) -![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401031639098.png) +对于最大编号是 $k$ 的所有环,记点 $k$ 逆时针方向的前一点为 $i$,顺时针方向的下个点为 $j$。由于 $dis[i,k]=g[i,k], dis[k,j]=g[k,j]$ 为定值,要使整个环最小,就要使 $dis[i,j]$ 最小。 -按 **环上编号最大点的编号** 为分类依据,分完类之后,只需要分别求一个每一类的最小值,然后求$min$所有最小值就是答案。 -每一类的最小值怎么求呢?我们来回顾一下$floyd$的过程: +$floyd$ 第一层循环到 $k$ 时的 $dis[i,j]$ 恰好是中间点只包含 $1\sim k−1$ 的最短距离。因此第 $k$ 类最小值可在此时得到。 -```cpp {.line-numbers} -for(int k=1;k<=n;k++) //K是要插入的点,dis[i][j]数组相当是知道了i~j的只经过1~k-1这些点的最小路径 - //此时在这个地方可以求第k类。从某个点连接到k - for(int i=1;i<=n;i++) - for(int j=1;j<=n;j++){ - ... - } -``` -![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401031648374.png) -枚举一下所有的点对$(i,j)$,固定了$(i,j)$之后,那么$i \rightarrow k$,$k \rightarrow j$的长度都是固定的。 -而左边那个弧的长度,就是$i \rightarrow j$在只有$1 \sim k-1$号点帮助下可以取得的最短距离,而这个距离恰好被保存在 **当前** 的$dis[i][j]$中。 +#### 状态表示 +![](https://cdn.acwing.com/media/article/image/2021/08/08/52559_1791f992f8-5.png) -也就是说,在正常进行$floyd$算法的第一层 -```cpp {.line-numbers} -for (int k = 1; k <= n ; k++){ - //这里需要加上一些DP的动作,利用floyd进行dp转移 - for (int i = 1; i < k; i++) - for (int j = i + 1; j < k; j++) - if (g[i][k] + g[k][j] < ans - dis[i][j]) // 减法防止爆INT - ans = dis[i][j] + g[i][k] + g[k][j]; - for (int i = 1; i <= n; i++) - for (int j = 1;j <=n; j++){ - .... - } -} -``` +#### 求方案 +$DP$ 求方案一般要 **记录转移前驱的所有维**。但 $floyd$ 转移方程中的 $k$ 表示路径的中间点,由于路径可以被两端和中间点覆盖,只要记下中间点,就能递归出路径。 -![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202401031650078.png) - -本题还有一个难点,就是$floyd$需要记录方案,其实就是求一下$d[i][j]$是由哪个中间点转移过来的。 -**** - -#### $Code$ +### 三、$floyd+dp+$递归输出路径 ```cpp {.line-numbers} -#include +#include +#include using namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; -int g[N][N], dis[N][N]; -vector path; +int g[N][N], d[N][N]; +int path[N], idx; int mid[N][N]; -int ans = INF; -// i->j之间的最短路径中途经点有哪些 void get_path(int i, int j) { - int k = mid[i][j]; // 获取中间转移点 - if (!k) return; // 如果i,j之间没有中间点,停止 - get_path(i, k); // 递归前半段 - path.push_back(k); // 记录k节点 - get_path(k, j); // 递归后半段 + int k = mid[i][j]; //获取中间转移点 + if (!k) return; //如果i,j之间没有中间点,停止 + get_path(i, k); // i->k + path[idx++] = k; //记录k节点 + get_path(k, j); // k->j } int main() { - // n个顶点,m条边 cin >> n >> m; - // 初始化邻接矩阵 memset(g, 0x3f, sizeof g); - for (int i = 1; i <= n; i++) g[i][i] = 0; // 邻接矩阵,自己到自己距离是0 + for (int i = 1; i <= n; i++) g[i][i] = 0; while (m--) { int a, b, c; cin >> a >> b >> c; - g[a][b] = g[b][a] = min(g[a][b], c); // 求最短路之类,(a,b)之间多条边输入只保留最短边 + g[a][b] = g[b][a] = min(g[a][b], c); } - // 把原始地图复制出来到生成最短距离dis - memcpy(dis, g, sizeof dis); - - for (int k = 1; k <= n; k++) { // 枚举每一个引入点k来连接缩短i,j的距离 + int ans = INF; + memcpy(d, g, sizeof d); + for (int k = 1; k <= n; k++) { + //插入DP计算 /* - Q1:为什么循环的时候i和j都需要小于k? - A:为了避免经过相同的点,比如i == k时,三个点就变成两个点了。 + Q:为什么循环的时候i和j都需要小于k啊,Floyd不是只需要经过的点小于k就可以了吗 + A:只是为了避免经过相同的点,比如i == k时,三个点就变成两个点了。 其实循环到n也是可以的,不过当i, j, k中有两个相同时就要continue一下 - - Q2:为什么非得把DP的这段代码嵌入到Floyd的整体代码中,不能先Floyd后再进行DP吗? - A:是不可以的。因为在进行插入节点号为k时,其实dis[i][j]中记录的是1~k-1插点后的最小距离, - 而不是全部插入点后的最短距离。 */ for (int i = 1; i < k; i++) for (int j = i + 1; j < k; j++) - if (g[i][k] + g[k][j] < ans - dis[i][j]) { // 减法防止爆INT - ans = dis[i][j] + g[i][k] + g[k][j]; - // 找到更小的环,需要记录路径,并且要求: 最小环的所有节点(按顺序输出) - // 顺序 - // 1. 上面的i,j枚举逻辑是j>i,所以i是第一个 - // 2. i->j 中间的路线不明,需要用get_path进行查询出i->j的最短路径怎么走,当然,也是在i,所以i是第一个 + // 2. i->j 中间的路线不明,需要用get_path进行探索 + // 3. 记录j + // 4. 记录k + idx = 0; + path[idx++] = i; + get_path(i, j); // i是怎么到达j的?就是问dist[i,j]是怎么获取到的,这是在求最短路径过程中的一个路径记录问题 + path[idx++] = j; + path[idx++] = k; } - // 正常floyd + //正常的floyd for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) - if (dis[i][j] > dis[i][k] + dis[k][j]) { - dis[i][j] = dis[i][k] + dis[k][j]; - mid[i][j] = k; // 记录路径i->j 是通过k进行转移的 + if (d[i][j] > d[i][k] + d[k][j]) { + d[i][j] = d[i][k] + d[k][j]; + mid[i][j] = k; //记录路径i->j 是通过k进行转移的 } } if (ans == INF) puts("No solution."); else - for (int i = 0; i < path.size(); i++) cout << path[i] << ' '; + for (int i = 0; i < idx; i++) cout << path[i] << ' '; return 0; } -``` \ No newline at end of file +``` + +### 四、关于三个$INF$相加爆$INT$的应对之道 +$Q1$:为什么这里是用$ans-dis[i,j]$,而不是写成 $ans> dis[i,j]+g[j,k]+g[k,i]$? +$A$: $g[j][k],g[k][i] ∈ l$,$l$是小于$500$的,所在 $g[j][k]+g[k][i]<1000$,肯定没问题 + $dis[i,j]$的初始值是$INF$,$g[i,j]$的初始值也是$INF$,如果都写在左边,如果$i,j,k$三者之间没有边,就是三个$INF$,累加和会爆掉$INT$,就会进入判断条件,错误. 而两个$INF$相加不会爆$INT$(想想松弛操作~) + +$Q2:(LL) dis[i][j] + g[j][k] + g[k][i] < ans$ 为什么是正确的?而 + $(LL) (dis[i][j] + g[j][k] + g[k][i]) < ans$为什么就是错误的? +$A$: + `INT_MAX = 2147483647` + `LONG LONG MAX=9223372036854775807ll` + + `INF = 0x3f3f3f3f = 1061109567` + `INF * 3 =1061109567 * 3 = 3183328701` 大于`INT_MAX`,即会爆`INT`,需要开`LONG LONG` + + `(LL)a + b + c` 将`a`转为`LL`,然后再加`b`加`c`,都是`LL+int`,在`LL`范围内,结果正确 + `(LL)(a + b + c)` 是先计算`a+b+c`,先爆`INT`,再转换`LL`,结果错误。 + +$Q3$: 所有数据全开$LL$为什么一样不对呢? +$A:$ +```c++ +memset(q, 0x3f, sizeof q); +cout << q[0] << endl; // 4557430888798830399 +cout << q[0] * 3 << endl; //-4774451407313060419 +``` + +因为问题出在$LL$的初始$memset$上,比如`memset(q,0x3f,sizeof q);` +此时,每个数组位置上的值是:$4557430888798830399$ +如果$i,j,k$三者之间没有关系,就会出现 类似于 `g[i,k]+g[k,j]+d[i,j]=3* 4557430888798830399`的情况,这个值太大,$LL$也装不下,值为`-4774451407313060419`,而此时$ans$等于$INF$,肯定满足小于条件,就进入了错误的判断逻辑。 + + +解决的办法有两种: +* `g[j][k] + g[k][i] < ans - dis[i][j]` 以减法避开三个$INF$相加,两个$INF$相加是$OK$的,不会爆$INT$ +* 将运算前的$dis[i][j]$转为$LL$,这样,三个$INF$不会爆$LL$ \ No newline at end of file diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp deleted file mode 100644 index 9e501a0..0000000 --- a/TangDou/AcWing_TiGao/T3/Floyd/344_2.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include -using namespace std; -const int N = 110; -#define INF 0x3f3f3f3f -/** -分析:模板题,理解floyd 的在 i , j 路径中没有包含k(因为此时k未用来更新),即可写出最小环 -*/ -int n, m; -int g[N][N]; -int dis[N][N]; // dp结果数组 -int path[N][N]; -int ans[N]; -int cnt; -int res = INF; -void floyd() { - for (int k = 1; k <= n; k++) { - // dp - for (int i = 1; i < k; i++) { - for (int j = i + 1; j < k; j++) { // i,j,k序号由小到大 - if (res - dis[i][j] > g[i][k] + g[k][j]) { // 减法防溢出 - res = dis[i][j] + g[i][k] + g[k][j]; - - int x = i, y = j; - cnt = 0; // 以前有过的路径也清空 - while (x != y) { - ans[cnt++] = y; - y = path[i][y]; - } - ans[cnt++] = x; - ans[cnt++] = k; // 序号最大的节点k - } - } - } - // floyd - for (int i = 1; i <= n; i++) - for (int j = 1; j <= n; j++) - if (dis[i][j] > dis[i][k] + dis[k][j]) { - dis[i][j] = dis[i][k] + dis[k][j]; - path[i][j] = path[k][j]; // 这咋还和我理解的不一样呢? - } - } -} - -int main() { - while (cin >> n >> m) { - // 邻接矩阵初始化 - for (int i = 1; i <= n; i++) - for (int j = 1; j <= n; j++) { - dis[i][j] = g[i][j] = INF; - path[i][j] = i; // 这里也是不一样,需要思考与整理 - } - - // 读入边 - while (m--) { - int a, b, c; - cin >> a >> b >> c; - g[a][b] = g[b][a] = min(g[a][b], c); - dis[a][b] = dis[b][a] = g[a][b]; - } - - floyd(); - - if (res == INF) { - puts("No solution."); - continue; - } - for (int i = 0; i < cnt; i++) printf("%d%s", ans[i], (i == cnt - 1) ? "\n" : " "); - } - return 0; -} \ No newline at end of file diff --git a/TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp b/TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp deleted file mode 100644 index cf3bcab..0000000 --- a/TangDou/AcWing_TiGao/T3/Floyd/344_3.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include -using namespace std; -const int N = 110; -#define INF 0x3f3f3f3f -/** -分析:模板题,理解floyd 的在 i , j 路径中没有包含k(因为此时k未用来更新),即可写出最小环 -*/ -int n, m; -int g[N][N]; -int dis[N][N]; // dp结果数组 -int path[N][N]; -int ans[N]; -int cnt; -int res = INF; -void floyd() { - for (int k = 1; k <= n; k++) { - // dp - for (int i = 1; i < k; i++) { - for (int j = i + 1; j < k; j++) { // i,j,k序号由小到大 - if (res - dis[i][j] > g[i][k] + g[k][j]) { // 减法防溢出 - res = dis[i][j] + g[i][k] + g[k][j]; - - int x = i, y = j; - cnt = 0; // 以前有过的路径也清空 - while (x != y) { - ans[cnt++] = y; - y = path[i][y]; - } - ans[cnt++] = x; - ans[cnt++] = k; // 序号最大的节点k - } - } - } - // floyd - for (int i = 1; i <= n; i++) - for (int j = 1; j <= n; j++) - if (dis[i][j] > dis[i][k] + dis[k][j]) { - dis[i][j] = dis[i][k] + dis[k][j]; - path[i][j] = path[k][j]; // 这咋还和我理解的不一样呢? - } - } -} - -int main() { - while (cin >> n >> m) { - // 邻接矩阵初始化 - for (int i = 1; i <= n; i++) - for (int j = 1; j <= n; j++) { - dis[i][j] = g[i][j] = INF; - path[i][j] = i; // 这里也是不一样,需要思考与整理 - } - - // 读入边 - while (m--) { - int a, b, c; - cin >> a >> b >> c; - g[a][b] = g[b][a] = min(g[a][b], c); - dis[a][b] = dis[b][a] = g[a][b]; - } - - floyd(); - - if (res == INF) { - puts("No solution."); - continue; - } - for (int i = 0; i < cnt; i++) printf("%d%s", ans[i], (i == cnt - 1) ? "\n" : " "); - } - return 0; -} diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 9c9ec72..2929523 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -27,7 +27,7 @@ void floyd() { - 遍历所有可能连接上的两个不在同一连通块中的点,尝试连接上这两个点后,得到可以获得到的最小直径。 - 原始直径与遍历尝试的所有可能直径$PK$,谁大谁是答案。 -### 三、判负环 +### 四、判负环 眼尖的人儿可能发现邻接矩阵 $g$ 中, $g[i][i]$并没有赋初值$0$,而是 $inf$。并且计算后 $g[i][i]$的值也不是 $0$,而是 $g[i][i]=g[i][u]+……+g[v][i]$,即从外面绕一圈回来的最短路径,而这正 **用于判断负圈**,即 $g[i][i]<0$。 @@ -101,7 +101,7 @@ int main() { } ``` -### 四、打印路径 +### 五、打印路径 #### [$HDU-1385$ $Minimum$ $Transport$ $Cost$](http://acm.hdu.edu.cn/showproblem.php?pid=1385) @@ -192,7 +192,7 @@ int main() { } ``` -### 五、最小环 +### 六、最小环 #### [$HDU$-$1599$ $find$ $the$ $mincost$ $route$](https://acm.hdu.edu.cn/showproblem.php?pid=1599) **类型: 最小环** @@ -272,7 +272,7 @@ signed main() { } } ``` -### 六、传递闭包 +### 七、传递闭包 #### [$HDU$-$1704$ $Rank$](https://acm.hdu.edu.cn/showproblem.php?pid=1704) @@ -319,9 +319,13 @@ int main() { return 0; } ``` +练习题 +#### [$AcWing$ $343$. 排序](https://www.cnblogs.com/littlehb/p/16029054.html) -### 七、变形 +这个更麻烦些,还需要输出大小的顺序。 + +### 八、变形 #### [$HDU$-$3631$ $Shortest$ $Path$](https://acm.hdu.edu.cn/showproblem.php?pid=3631) **题意** @@ -390,7 +394,7 @@ int main() { } ``` -### 八、其它习题 +### 九、其它习题 #### $SSL-1760$(商店选址) **题目** From f6f278c5dc45ad240cbb2d4b442967177e837a5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Fri, 5 Jan 2024 16:18:18 +0800 Subject: [PATCH 87/87] 'commit' --- TangDou/AcWing_TiGao/T3/Floyd/345.md | 6 +++--- TangDou/Topic/【Floyd专题】.md | 9 ++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/TangDou/AcWing_TiGao/T3/Floyd/345.md b/TangDou/AcWing_TiGao/T3/Floyd/345.md index 4d48988..63f73c5 100644 --- a/TangDou/AcWing_TiGao/T3/Floyd/345.md +++ b/TangDou/AcWing_TiGao/T3/Floyd/345.md @@ -101,9 +101,9 @@ t = [ 14, 11, 14 ] 矩阵乘法 **模板** 如下: ```cpp {.line-numbers} -for (int k = 1; k <= COLS_A; ++k) - for (int i = 1; i <= ROWS_A; ++i) - for (int j = 1; j <= COLS_B; ++j) +for (int k = 1; k <= COLS_A; k++) + for (int i = 1; i <= ROWS_A; i++) + for (int j = 1; j <= COLS_B; j++) t[i][j] += a[i][k] * b[k][j]; ``` diff --git a/TangDou/Topic/【Floyd专题】.md b/TangDou/Topic/【Floyd专题】.md index 2929523..7d7becb 100644 --- a/TangDou/Topic/【Floyd专题】.md +++ b/TangDou/Topic/【Floyd专题】.md @@ -272,6 +272,10 @@ signed main() { } } ``` + **练习题** + #### [$AcWing$ $344$. 观光之旅](https://www.cnblogs.com/littlehb/p/16033489.html) + + ### 七、传递闭包 #### [$HDU$-$1704$ $Rank$](https://acm.hdu.edu.cn/showproblem.php?pid=1704) @@ -393,8 +397,11 @@ int main() { return 0; } ``` +### 九、限定边数量的情况下求多源最短路径 + +#### [$AcWing$ $345$ 牛站](https://www.cnblogs.com/littlehb/p/16043039.html) -### 九、其它习题 +### 十、其它习题 #### $SSL-1760$(商店选址) **题目**